Index: projects/clang380-import/COPYRIGHT =================================================================== --- projects/clang380-import/COPYRIGHT (revision 293015) +++ projects/clang380-import/COPYRIGHT (revision 293016) @@ -1,126 +1,126 @@ # $FreeBSD$ # @(#)COPYRIGHT 8.2 (Berkeley) 3/21/94 The compilation of software known as FreeBSD is distributed under the following terms: -Copyright (c) 1992-2015 The FreeBSD Project. All rights reserved. +Copyright (c) 1992-2016 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. The 4.4BSD and 4.4BSD-Lite software is distributed under the following terms: All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite Releases is copyrighted by The Regents of the University of California. Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. 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 the University of California, Berkeley and its contributors. 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The Institute of Electrical and Electronics Engineers and the American National Standards Committee X3, on Information Processing Systems have given us permission to reprint portions of their documentation. In the following statement, the phrase ``this text'' refers to portions of the system documentation. Portions of this text are reprinted and reproduced in electronic form in the second BSD Networking Software Release, from IEEE Std 1003.1-1988, IEEE Standard Portable Operating System Interface for Computer Environments (POSIX), copyright C 1988 by the Institute of Electrical and Electronics Engineers, Inc. In the event of any discrepancy between these versions and the original IEEE Standard, the original IEEE Standard is the referee document. In the following statement, the phrase ``This material'' refers to portions of the system documentation. This material is reproduced with permission from American National Standards Committee X3, on Information Processing Systems. Computer and Business Equipment Manufacturers Association (CBEMA), 311 First St., NW, Suite 500, Washington, DC 20001-2178. The developmental work of Programming Language C was completed by the X3J11 Technical Committee. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the Regents of the University of California. NOTE: The copyright of UC Berkeley's Berkeley Software Distribution ("BSD") source has been updated. The copyright addendum may be found at ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change and is included below. July 22, 1999 To All Licensees, Distributors of Any Version of BSD: As you know, certain of the Berkeley Software Distribution ("BSD") source code files require that further distributions of products containing all or portions of the software, acknowledge within their advertising materials that such products contain software developed by UC Berkeley and its contributors. Specifically, the provision reads: " * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors." Effective immediately, licensees and distributors are no longer required to include the acknowledgement within advertising materials. Accordingly, the foregoing paragraph of those BSD Unix files containing it is hereby deleted in its entirety. William Hoskins Director, Office of Technology Licensing University of California, Berkeley Index: projects/clang380-import/bin/sh/expand.c =================================================================== --- projects/clang380-import/bin/sh/expand.c (revision 293015) +++ projects/clang380-import/bin/sh/expand.c (revision 293016) @@ -1,1662 +1,1539 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1997-2005 * Herbert Xu . All rights reserved. + * Copyright (c) 2010-2015 + * Jilles Tjoelker . All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Routines to expand arguments to commands. We have to deal with * backquotes, shell variables, and file metacharacters. */ #include "shell.h" #include "main.h" #include "nodes.h" #include "eval.h" #include "expand.h" #include "syntax.h" #include "parser.h" #include "jobs.h" #include "options.h" #include "var.h" #include "input.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "mystring.h" #include "arith.h" #include "show.h" #include "builtins.h" -/* - * Structure specifying which parts of the string should be searched - * for IFS characters. - */ +enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK }; -struct ifsregion { - struct ifsregion *next; /* next region in list */ - int begoff; /* offset of start of region */ - int endoff; /* offset of end of region */ - int inquotes; /* search for nul bytes only */ +struct worddest { + struct arglist *list; + enum wordstate state; }; - static char *expdest; /* output of current string */ static struct nodelist *argbackq; /* list of back quote expressions */ -static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ -static struct ifsregion *ifslastp; /* last struct in list */ -static char *argstr(char *, int); +static char *argstr(char *, int, struct worddest *); static char *exptilde(char *, int); -static char *expari(char *); -static void expbackq(union node *, int, int); -static int subevalvar_trim(char *, int, int, int, int); +static char *expari(char *, int, struct worddest *); +static void expbackq(union node *, int, int, struct worddest *); +static void subevalvar_trim(char *, int, int, int); static int subevalvar_misc(char *, const char *, int, int, int); -static char *evalvar(char *, int); +static char *evalvar(char *, int, struct worddest *); static int varisset(const char *, int); -static void strtodest(const char *, int, int, int); -static void varvalue(const char *, int, int, int); -static void recordregion(int, int, int); -static void removerecordregions(int); -static void ifsbreakup(char *, struct arglist *); -static void expandmeta(struct arglist *, struct arglist *); +static void strtodest(const char *, int, int, int, struct worddest *); +static void reprocess(int, int, int, int, struct worddest *); +static void varvalue(const char *, int, int, int, struct worddest *); +static void expandmeta(char *, struct arglist *); static void expmeta(char *, char *, struct arglist *); static int expsortcmp(const void *, const void *); -static int patmatch(const char *, const char *, int); -static char *cvtnum(int, char *); +static int patmatch(const char *, const char *); +static void cvtnum(int, char *); static int collate_range_cmp(wchar_t, wchar_t); void emptyarglist(struct arglist *list) { list->args = list->smallarg; list->count = 0; list->capacity = sizeof(list->smallarg) / sizeof(list->smallarg[0]); } void appendarglist(struct arglist *list, char *str) { char **newargs; int newcapacity; if (list->count >= list->capacity) { newcapacity = list->capacity * 2; if (newcapacity < 16) newcapacity = 16; if (newcapacity > INT_MAX / (int)sizeof(newargs[0])) error("Too many entries in arglist"); newargs = stalloc(newcapacity * sizeof(newargs[0])); memcpy(newargs, list->args, list->count * sizeof(newargs[0])); list->args = newargs; list->capacity = newcapacity; } list->args[list->count++] = str; } static int collate_range_cmp(wchar_t c1, wchar_t c2) { static wchar_t s1[2], s2[2]; s1[0] = c1; s2[0] = c2; return (wcscoll(s1, s2)); } static char * stputs_quotes(const char *data, const char *syntax, char *p) { while (*data) { CHECKSTRSPACE(2, p); if (syntax[(int)*data] == CCTL) USTPUTC(CTLESC, p); USTPUTC(*data++, p); } return (p); } #define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) +static char * +nextword(char c, int flag, char *p, struct worddest *dst) +{ + int is_ws; + + is_ws = c == '\t' || c == '\n' || c == ' '; + if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK : + dst->state != WORD_WS_DELIMITED) || c == '\0') { + STPUTC('\0', p); + if (flag & EXP_GLOB) + expandmeta(grabstackstr(p), dst->list); + else + appendarglist(dst->list, grabstackstr(p)); + dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE; + } else if (!is_ws && dst->state == WORD_WS_DELIMITED) + dst->state = WORD_IDLE; + /* Reserve space while the stack string is empty. */ + appendarglist(dst->list, NULL); + dst->list->count--; + STARTSTACKSTR(p); + return p; +} +#define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist) + +static char * +stputs_split(const char *data, const char *syntax, int flag, char *p, + struct worddest *dst) +{ + const char *ifs; + char c; + + ifs = ifsset() ? ifsval() : " \t\n"; + while (*data) { + CHECKSTRSPACE(2, p); + c = *data++; + if (strchr(ifs, c) != NULL) { + NEXTWORD(c, flag, p, dst); + continue; + } + if (flag & EXP_GLOB && syntax[(int)c] == CCTL) + USTPUTC(CTLESC, p); + USTPUTC(c, p); + } + return (p); +} +#define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst) + /* * Perform expansions on an argument, placing the resulting list of arguments * in arglist. Parameter expansion, command substitution and arithmetic * expansion are always performed; additional expansions can be requested * via flag (EXP_*). * The result is left in the stack string. * When arglist is NULL, perform here document expansion. * * Caution: this function uses global state and is not reentrant. * However, a new invocation after an interrupted invocation is safe * and will reset the global state for the new call. */ void expandarg(union node *arg, struct arglist *arglist, int flag) { - struct arglist exparg; - char *p; + struct worddest exparg; + if (fflag) + flag &= ~EXP_GLOB; argbackq = arg->narg.backquote; + exparg.list = arglist; + exparg.state = WORD_IDLE; STARTSTACKSTR(expdest); - ifsfirst.next = NULL; - ifslastp = NULL; - argstr(arg->narg.text, flag); + argstr(arg->narg.text, flag, &exparg); if (arglist == NULL) { STACKSTRNUL(expdest); return; /* here document expanded */ } - STPUTC('\0', expdest); - p = grabstackstr(expdest); - emptyarglist(&exparg); - if (flag & EXP_FULL) { - ifsbreakup(p, &exparg); - expandmeta(&exparg, arglist); - } else - appendarglist(arglist, p); - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; + if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() || + exparg.state == WORD_QUOTEMARK) { + STPUTC('\0', expdest); + if (flag & EXP_SPLIT) { + if (flag & EXP_GLOB) + expandmeta(grabstackstr(expdest), exparg.list); + else + appendarglist(exparg.list, grabstackstr(expdest)); + } } + if ((flag & EXP_SPLIT) == 0) + appendarglist(arglist, grabstackstr(expdest)); } /* * Perform parameter expansion, command substitution and arithmetic * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. * This is used to expand word in ${var+word} etc. - * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC + * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC * characters to allow for further processing. - * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. + * + * If EXP_SPLIT is set, dst receives any complete words produced. */ static char * -argstr(char *p, int flag) +argstr(char *p, int flag, struct worddest *dst) { char c; - int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */ int firsteq = 1; int split_lit; int lit_quoted; split_lit = flag & EXP_SPLIT_LIT; lit_quoted = flag & EXP_LIT_QUOTED; flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { CHECKSTRSPACE(2, expdest); switch (c = *p++) { case '\0': return (p - 1); case CTLENDVAR: case CTLENDARI: return (p); case CTLQUOTEMARK: lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && p[2] == '@' && p[3] == '=') break; - if ((flag & EXP_FULL) != 0) - USTPUTC(c, expdest); + if ((flag & EXP_SPLIT) != 0 && expdest == stackblock()) + dst->state = WORD_QUOTEMARK; break; case CTLQUOTEEND: lit_quoted = 0; break; case CTLESC: - if (quotes) - USTPUTC(c, expdest); c = *p++; + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } + if (quotes) + USTPUTC(CTLESC, expdest); USTPUTC(c, expdest); - if (split_lit && !lit_quoted) - recordregion(expdest - stackblock() - - (quotes ? 2 : 1), - expdest - stackblock(), 0); break; case CTLVAR: - p = evalvar(p, flag); + p = evalvar(p, flag, dst); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: - expbackq(argbackq->n, c & CTLQUOTE, flag); + expbackq(argbackq->n, c & CTLQUOTE, flag, dst); argbackq = argbackq->next; break; case CTLARI: - p = expari(p); + p = expari(p, flag, dst); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } USTPUTC(c, expdest); - if (split_lit && !lit_quoted) - recordregion(expdest - stackblock() - 1, - expdest - stackblock(), 0); if (flag & EXP_VARTILDE && *p == '~' && (c != '=' || firsteq)) { if (c == '=') firsteq = 0; p = exptilde(p, flag); } break; default: + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } USTPUTC(c, expdest); - if (split_lit && !lit_quoted) - recordregion(expdest - stackblock() - 1, - expdest - stackblock(), 0); } } } /* * Perform tilde expansion, placing the result in the stack string and * returning the next position in the input string to process. */ static char * exptilde(char *p, int flag) { char c, *startp = p; struct passwd *pw; char *home; for (;;) { c = *p; switch(c) { case CTLESC: /* This means CTL* are always considered quoted. */ case CTLVAR: case CTLBACKQ: case CTLBACKQ | CTLQUOTE: case CTLARI: case CTLENDARI: case CTLQUOTEMARK: return (startp); case ':': if ((flag & EXP_VARTILDE) == 0) break; /* FALLTHROUGH */ case '\0': case '/': case CTLENDVAR: *p = '\0'; if (*(startp+1) == '\0') { home = lookupvar("HOME"); } else { pw = getpwnam(startp+1); home = pw != NULL ? pw->pw_dir : NULL; } *p = c; if (home == NULL || *home == '\0') return (startp); - strtodest(home, flag, VSNORMAL, 1); + strtodest(home, flag, VSNORMAL, 1, NULL); return (p); } p++; } } -static void -removerecordregions(int endoff) -{ - if (ifslastp == NULL) - return; - - if (ifsfirst.endoff > endoff) { - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - if (ifsfirst.begoff > endoff) - ifslastp = NULL; - else { - ifslastp = &ifsfirst; - ifsfirst.endoff = endoff; - } - return; - } - - ifslastp = &ifsfirst; - while (ifslastp->next && ifslastp->next->begoff < endoff) - ifslastp=ifslastp->next; - while (ifslastp->next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifslastp->next->next; - ckfree(ifslastp->next); - ifslastp->next = ifsp; - INTON; - } - if (ifslastp->endoff > endoff) - ifslastp->endoff = endoff; -} - /* * Expand arithmetic expression. - * Note that flag is not required as digits never require CTLESC characters. */ static char * -expari(char *p) +expari(char *p, int flag, struct worddest *dst) { char *q, *start; arith_t result; int begoff; int quoted; int adj; quoted = *p++ == '"'; begoff = expdest - stackblock(); - p = argstr(p, 0); - removerecordregions(begoff); + p = argstr(p, 0, NULL); STPUTC('\0', expdest); start = stackblock() + begoff; q = grabstackstr(expdest); result = arith(start); ungrabstackstr(q, expdest); start = stackblock() + begoff; adj = start - expdest; STADJUST(adj, expdest); CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); adj = strlen(expdest); STADJUST(adj, expdest); if (!quoted) - recordregion(begoff, expdest - stackblock(), 0); + reprocess(expdest - adj - stackblock(), flag, VSNORMAL, 0, dst); return p; } /* * Perform command substitution. */ static void -expbackq(union node *cmd, int quoted, int flag) +expbackq(union node *cmd, int quoted, int flag, struct worddest *dst) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; - struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; - int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; - int quotes = flag & (EXP_FULL | EXP_CASE); + int quotes = flag & (EXP_GLOB | EXP_CASE); size_t nnl; + const char *ifs; INTOFF; - saveifs = ifsfirst; - savelastp = ifslastp; saveargbackq = argbackq; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); - ifsfirst = saveifs; - ifslastp = savelastp; argbackq = saveargbackq; p = in.buf; lastc = '\0'; nnl = 0; + if (!quoted && flag & EXP_SPLIT) + ifs = ifsset() ? ifsval() : " \t\n"; + else + ifs = ""; /* Don't copy trailing newlines */ for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; - if (lastc != '\0') { - if (lastc == '\n') { - nnl++; - } else { - CHECKSTRSPACE(nnl + 2, dest); - while (nnl > 0) { - nnl--; - USTPUTC('\n', dest); + if (lastc == '\0') + continue; + if (lastc == '\n') { + nnl++; + } else { + if (nnl > 0) { + if (strchr(ifs, '\n') != NULL) { + NEXTWORD('\n', flag, dest, dst); + nnl = 0; + } else { + CHECKSTRSPACE(nnl + 2, dest); + while (nnl > 0) { + nnl--; + USTPUTC('\n', dest); + } } + } + if (strchr(ifs, lastc) != NULL) + NEXTWORD(lastc, flag, dest, dst); + else { + CHECKSTRSPACE(2, dest); if (quotes && syntax[(int)lastc] == CCTL) USTPUTC(CTLESC, dest); USTPUTC(lastc, dest); } } } if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) exitstatus = waitforjob(in.jp, (int *)NULL); - if (quoted == 0) - recordregion(startloc, dest - stackblock(), 0); TRACE(("expbackq: size=%td: \"%.*s\"\n", ((dest - stackblock()) - startloc), (int)((dest - stackblock()) - startloc), stackblock() + startloc)); expdest = dest; INTON; } static void recordleft(const char *str, const char *loc, char *startp) { int amount; amount = ((str - 1) - (loc - startp)) - expdest; STADJUST(amount, expdest); while (loc != str - 1) *startp++ = *loc++; } -static int -subevalvar_trim(char *p, int strloc, int subtype, int startloc, int quotes) +static void +subevalvar_trim(char *p, int strloc, int subtype, int startloc) { char *startp; char *loc = NULL; - char *q; char *str; int c = 0; struct nodelist *saveargbackq = argbackq; int amount; - argstr(p, EXP_CASE | EXP_TILDE); + argstr(p, EXP_CASE | EXP_TILDE, NULL); STACKSTRNUL(expdest); argbackq = saveargbackq; startp = stackblock() + startloc; str = stackblock() + strloc; switch (subtype) { case VSTRIMLEFT: for (loc = startp; loc < str; loc++) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, quotes)) { + if (patmatch(str, startp)) { *loc = c; recordleft(str, loc, startp); - return 1; + return; } *loc = c; - if (quotes && *loc == CTLESC) - loc++; } - return 0; + break; case VSTRIMLEFTMAX: for (loc = str - 1; loc >= startp;) { c = *loc; *loc = '\0'; - if (patmatch(str, startp, quotes)) { + if (patmatch(str, startp)) { *loc = c; recordleft(str, loc, startp); - return 1; + return; } *loc = c; loc--; - if (quotes && loc > startp && *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } } - return 0; + break; case VSTRIMRIGHT: for (loc = str - 1; loc >= startp;) { - if (patmatch(str, loc, quotes)) { + if (patmatch(str, loc)) { amount = loc - expdest; STADJUST(amount, expdest); - return 1; + return; } loc--; - if (quotes && loc > startp && *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } } - return 0; + break; case VSTRIMRIGHTMAX: for (loc = startp; loc < str - 1; loc++) { - if (patmatch(str, loc, quotes)) { + if (patmatch(str, loc)) { amount = loc - expdest; STADJUST(amount, expdest); - return 1; + return; } - if (quotes && *loc == CTLESC) - loc++; } - return 0; + break; default: abort(); } + amount = (expdest - stackblock() - strloc) + 1; + STADJUST(-amount, expdest); } static int subevalvar_misc(char *p, const char *var, int subtype, int startloc, int varflags) { char *startp; struct nodelist *saveargbackq = argbackq; int amount; - argstr(p, EXP_TILDE); + argstr(p, EXP_TILDE, NULL); STACKSTRNUL(expdest); argbackq = saveargbackq; startp = stackblock() + startloc; switch (subtype) { case VSASSIGN: setvar(var, startp, 0); amount = startp - expdest; STADJUST(amount, expdest); return 1; case VSQUESTION: if (*p != CTLENDVAR) { outfmt(out2, "%s\n", startp); error((char *)NULL); } error("%.*s: parameter %snot set", (int)(p - var - 1), var, (varflags & VSNUL) ? "null or " : ""); return 0; default: abort(); } } /* * Expand a variable, and return a pointer to the next character in the * input string. */ static char * -evalvar(char *p, int flag) +evalvar(char *p, int flag, struct worddest *dst) { int subtype; int varflags; char *var; const char *val; int patloc; int c; int set; int special; int startloc; int varlen; int varlenb; - int easy; - int quotes = flag & (EXP_FULL | EXP_CASE); - int record = 0; + char buf[21]; varflags = (unsigned char)*p++; subtype = varflags & VSTYPE; var = p; special = 0; if (! is_name(*p)) special = 1; p = strchr(p, '=') + 1; again: /* jump here after setting a variable with ${var=text} */ if (varflags & VSLINENO) { set = 1; special = 1; val = NULL; } else if (special) { set = varisset(var, varflags & VSNUL); val = NULL; } else { val = bltinlookup(var, 1); if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { val = NULL; set = 0; } else set = 1; } varlen = 0; startloc = expdest - stackblock(); if (!set && uflag && *var != '@' && *var != '*') { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: case VSLENGTH: error("%.*s: parameter not set", (int)(p - var - 1), var); } } if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { - if (varflags & VSLINENO) - STPUTBIN(var, p - var - 1, expdest); - else - varvalue(var, varflags & VSQUOTE, subtype, flag); + if (varflags & VSLINENO) { + if (p - var > (ptrdiff_t)sizeof(buf)) + abort(); + memcpy(buf, var, p - var - 1); + buf[p - var - 1] = '\0'; + strtodest(buf, flag, subtype, + varflags & VSQUOTE, dst); + } else + varvalue(var, varflags & VSQUOTE, subtype, flag, + dst); if (subtype == VSLENGTH) { varlenb = expdest - stackblock() - startloc; varlen = varlenb; if (localeisutf8) { val = stackblock() + startloc; for (;val != expdest; val++) if ((*val & 0xC0) == 0x80) varlen--; } STADJUST(-varlenb, expdest); } } else { if (subtype == VSLENGTH) { for (;*val; val++) if (!localeisutf8 || (*val & 0xC0) != 0x80) varlen++; } else strtodest(val, flag, subtype, - varflags & VSQUOTE); + varflags & VSQUOTE, dst); } } if (subtype == VSPLUS) set = ! set; - easy = ((varflags & VSQUOTE) == 0 || - (*var == '@' && shellparam.nparam != 1)); - - switch (subtype) { case VSLENGTH: - expdest = cvtnum(varlen, expdest); - record = 1; + cvtnum(varlen, buf); + strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst); break; case VSNORMAL: - record = easy; break; case VSPLUS: case VSMINUS: if (!set) { - argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | - (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); + argstr(p, flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) | + (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst); break; } - record = easy; break; case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: if (!set) break; /* * Terminate the string and start recording the pattern * right after it */ STPUTC('\0', expdest); patloc = expdest - stackblock(); - if (subevalvar_trim(p, patloc, subtype, - startloc, quotes) == 0) { - int amount = (expdest - stackblock() - patloc) + 1; - STADJUST(-amount, expdest); - } - /* Remove any recorded regions beyond start of variable */ - removerecordregions(startloc); - record = 1; + subevalvar_trim(p, patloc, subtype, startloc); + reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst); + if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE) + dst->state = WORD_QUOTEMARK; break; case VSASSIGN: case VSQUESTION: if (!set) { if (subevalvar_misc(p, var, subtype, startloc, varflags)) { varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); goto again; } break; } - record = easy; break; case VSERROR: c = p - var - 1; error("${%.*s%s}: Bad substitution", c, var, (c > 0 && *p != CTLENDVAR) ? "..." : ""); default: abort(); } - if (record) - recordregion(startloc, expdest - stackblock(), - varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' && - (*var == '@' || *var == '*'))); - if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { if ((c = *p++) == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (set) argbackq = argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p; } /* * Test whether a specialized variable is set. */ static int varisset(const char *name, int nulok) { if (*name == '!') return backgndpidset(); else if (*name == '@' || *name == '*') { if (*shellparam.p == NULL) return 0; if (nulok) { char **av; for (av = shellparam.p; *av; av++) if (**av != '\0') return 1; return 0; } } else if (is_digit(*name)) { char *ap; long num; errno = 0; num = strtol(name, NULL, 10); if (errno != 0 || num > shellparam.nparam) return 0; if (num == 0) ap = arg0; else ap = shellparam.p[num - 1]; if (nulok && (ap == NULL || *ap == '\0')) return 0; } return 1; } static void -strtodest(const char *p, int flag, int subtype, int quoted) +strtodest(const char *p, int flag, int subtype, int quoted, + struct worddest *dst) { - if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) + if (subtype == VSLENGTH || subtype == VSTRIMLEFT || + subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT || + subtype == VSTRIMRIGHTMAX) + STPUTS(p, expdest); + else if (flag & EXP_SPLIT && !quoted && dst != NULL) + STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst); + else if (flag & (EXP_GLOB | EXP_CASE)) STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); else STPUTS(p, expdest); } +static void +reprocess(int startloc, int flag, int subtype, int quoted, + struct worddest *dst) +{ + static char *buf = NULL; + static size_t buflen = 0; + char *startp; + size_t len, zpos, zlen; + + startp = stackblock() + startloc; + len = expdest - startp; + if (len >= SIZE_MAX / 2) + abort(); + INTOFF; + if (len >= buflen) { + ckfree(buf); + buf = NULL; + } + if (buflen < 128) + buflen = 128; + while (len >= buflen) + buflen <<= 1; + if (buf == NULL) + buf = ckmalloc(buflen); + INTON; + memcpy(buf, startp, len); + buf[len] = '\0'; + STADJUST(-len, expdest); + for (zpos = 0;;) { + zlen = strlen(buf + zpos); + strtodest(buf + zpos, flag, subtype, quoted, dst); + zpos += zlen + 1; + if (zpos == len + 1) + break; + if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len))) + NEXTWORD('\0', flag, expdest, dst); + } +} + /* * Add the value of a specialized variable to the stack string. */ static void -varvalue(const char *name, int quoted, int subtype, int flag) +varvalue(const char *name, int quoted, int subtype, int flag, + struct worddest *dst) { int num; char *p; int i; + int splitlater; char sep[2]; char **ap; + char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1]; + if (subtype == VSLENGTH) + flag &= ~EXP_FULL; + splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || + subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX; + switch (*name) { case '$': num = rootpid; break; case '?': num = oexitstatus; break; case '#': num = shellparam.nparam; break; case '!': num = backgndpidval(); break; case '-': + p = buf; for (i = 0 ; i < NSHORTOPTS ; i++) { if (optlist[i].val) - STPUTC(optlist[i].letter, expdest); + *p++ = optlist[i].letter; } + *p = '\0'; + strtodest(buf, flag, subtype, quoted, dst); return; case '@': - if (flag & EXP_FULL && quoted) { + if (flag & EXP_SPLIT && quoted) { for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - strtodest(p, flag, subtype, quoted); - if (*ap) - STPUTC('\0', expdest); + strtodest(p, flag, subtype, quoted, dst); + if (*ap) { + if (splitlater) + STPUTC('\0', expdest); + else + NEXTWORD('\0', flag, expdest, + dst); + } } + if (shellparam.nparam > 0) + dst->state = WORD_QUOTEMARK; return; } /* FALLTHROUGH */ case '*': if (ifsset()) sep[0] = ifsval()[0]; else sep[0] = ' '; sep[1] = '\0'; for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - strtodest(p, flag, subtype, quoted); + strtodest(p, flag, subtype, quoted, dst); if (!*ap) break; if (sep[0]) - strtodest(sep, flag, subtype, quoted); - else if (flag & EXP_FULL && !quoted && **ap != '\0') - STPUTC('\0', expdest); + strtodest(sep, flag, subtype, quoted, dst); + else if (flag & EXP_SPLIT && !quoted && **ap != '\0') { + if (splitlater) + STPUTC('\0', expdest); + else + NEXTWORD('\0', flag, expdest, dst); + } } return; default: if (is_digit(*name)) { num = atoi(name); if (num == 0) p = arg0; else if (num > 0 && num <= shellparam.nparam) p = shellparam.p[num - 1]; else return; - strtodest(p, flag, subtype, quoted); + strtodest(p, flag, subtype, quoted, dst); } return; } - expdest = cvtnum(num, expdest); + cvtnum(num, buf); + strtodest(buf, flag, subtype, quoted, dst); } -/* - * Record the fact that we have to scan this region of the - * string for IFS characters. - */ - -static void -recordregion(int start, int end, int inquotes) -{ - struct ifsregion *ifsp; - - INTOFF; - if (ifslastp == NULL) { - ifsp = &ifsfirst; - } else { - if (ifslastp->endoff == start - && ifslastp->inquotes == inquotes) { - /* extend previous area */ - ifslastp->endoff = end; - INTON; - return; - } - ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); - ifslastp->next = ifsp; - } - ifslastp = ifsp; - ifslastp->next = NULL; - ifslastp->begoff = start; - ifslastp->endoff = end; - ifslastp->inquotes = inquotes; - INTON; -} - - - -/* - * Break the argument string into pieces based upon IFS and add the - * strings to the argument list. The regions of the string to be - * searched for IFS characters have been stored by recordregion. - * CTLESC characters are preserved but have little effect in this pass - * other than escaping CTL* characters. In particular, they do not escape - * IFS characters: that should be done with the ifsregion mechanism. - * CTLQUOTEMARK characters are used to preserve empty quoted strings. - * This pass treats them as a regular character, making the string non-empty. - * Later, they are removed along with the other CTL* characters. - */ -static void -ifsbreakup(char *string, struct arglist *arglist) -{ - struct ifsregion *ifsp; - char *start; - char *p; - char *q; - const char *ifs; - const char *ifsspc; - int had_param_ch = 0; - - start = string; - - if (ifslastp == NULL) { - /* Return entire argument, IFS doesn't apply to any of it */ - appendarglist(arglist, start); - return; - } - - ifs = ifsset() ? ifsval() : " \t\n"; - - for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { - p = string + ifsp->begoff; - while (p < string + ifsp->endoff) { - q = p; - if (*p == CTLESC) - p++; - if (ifsp->inquotes) { - /* Only NULs (should be from "$@") end args */ - had_param_ch = 1; - if (*p != 0) { - p++; - continue; - } - ifsspc = NULL; - } else { - if (!strchr(ifs, *p)) { - had_param_ch = 1; - p++; - continue; - } - ifsspc = strchr(" \t\n", *p); - - /* Ignore IFS whitespace at start */ - if (q == start && ifsspc != NULL) { - p++; - start = p; - continue; - } - had_param_ch = 0; - } - - /* Save this argument... */ - *q = '\0'; - appendarglist(arglist, start); - p++; - - if (ifsspc != NULL) { - /* Ignore further trailing IFS whitespace */ - for (; p < string + ifsp->endoff; p++) { - q = p; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p) == NULL) { - p = q; - break; - } - if (strchr(" \t\n", *p) == NULL) { - p++; - break; - } - } - } - start = p; - } - } - - /* - * Save anything left as an argument. - * Traditionally we have treated 'IFS=':'; set -- x$IFS' as - * generating 2 arguments, the second of which is empty. - * Some recent clarification of the Posix spec say that it - * should only generate one.... - */ - if (had_param_ch || *start != 0) - appendarglist(arglist, start); -} - - static char expdir[PATH_MAX]; #define expdir_end (expdir + sizeof(expdir)) /* * Perform pathname generation and remove control characters. - * At this point, the only control characters should be CTLESC and CTLQUOTEMARK. + * At this point, the only control characters should be CTLESC. * The results are stored in the list dstlist. */ static void -expandmeta(struct arglist *srclist, struct arglist *dstlist) +expandmeta(char *pattern, struct arglist *dstlist) { char *p; int firstmatch; - int i; char c; - for (i = 0; i < srclist->count; i++) { - firstmatch = dstlist->count; - if (!fflag) { - p = srclist->args[i]; - for (; (c = *p) != '\0'; p++) { - /* fast check for meta chars */ - if (c == '*' || c == '?' || c == '[') { - INTOFF; - expmeta(expdir, srclist->args[i], - dstlist); - INTON; - break; - } - } + firstmatch = dstlist->count; + p = pattern; + for (; (c = *p) != '\0'; p++) { + /* fast check for meta chars */ + if (c == '*' || c == '?' || c == '[') { + INTOFF; + expmeta(expdir, pattern, dstlist); + INTON; + break; } - if (dstlist->count == firstmatch) { - /* - * no matches - */ - rmescapes(srclist->args[i]); - appendarglist(dstlist, srclist->args[i]); - } else { - qsort(&dstlist->args[firstmatch], - dstlist->count - firstmatch, - sizeof(dstlist->args[0]), expsortcmp); - } } + if (dstlist->count == firstmatch) { + /* + * no matches + */ + rmescapes(pattern); + appendarglist(dstlist, pattern); + } else { + qsort(&dstlist->args[firstmatch], + dstlist->count - firstmatch, + sizeof(dstlist->args[0]), expsortcmp); + } } /* * Do metacharacter (i.e. *, ?, [...]) expansion. */ static void expmeta(char *enddir, char *name, struct arglist *arglist) { const char *p; const char *q; const char *start; char *endname; int metaflag; struct stat statb; DIR *dirp; struct dirent *dp; int atend; int matchdot; int esc; int namlen; metaflag = 0; start = name; for (p = name; esc = 0, *p; p += esc + 1) { if (*p == '*' || *p == '?') metaflag = 1; else if (*p == '[') { q = p + 1; if (*q == '!' || *q == '^') q++; for (;;) { - while (*q == CTLQUOTEMARK) - q++; if (*q == CTLESC) q++; if (*q == '/' || *q == '\0') break; if (*++q == ']') { metaflag = 1; break; } } } else if (*p == '\0') break; - else if (*p == CTLQUOTEMARK) - continue; else { if (*p == CTLESC) esc++; if (p[esc] == '/') { if (metaflag) break; start = p + esc + 1; } } } if (metaflag == 0) { /* we've reached the end of the file name */ if (enddir != expdir) metaflag++; for (p = name ; ; p++) { - if (*p == CTLQUOTEMARK) - continue; if (*p == CTLESC) p++; *enddir++ = *p; if (*p == '\0') break; if (enddir == expdir_end) return; } if (metaflag == 0 || lstat(expdir, &statb) >= 0) appendarglist(arglist, stsavestr(expdir)); return; } endname = name + (p - name); if (start != name) { p = name; while (p < start) { - while (*p == CTLQUOTEMARK) - p++; if (*p == CTLESC) p++; *enddir++ = *p++; if (enddir == expdir_end) return; } } if (enddir == expdir) { p = "."; } else if (enddir == expdir + 1 && *expdir == '/') { p = "/"; } else { p = expdir; enddir[-1] = '\0'; } if ((dirp = opendir(p)) == NULL) return; if (enddir != expdir) enddir[-1] = '/'; if (*endname == 0) { atend = 1; } else { atend = 0; *endname = '\0'; endname += esc + 1; } matchdot = 0; p = start; - while (*p == CTLQUOTEMARK) - p++; if (*p == CTLESC) p++; if (*p == '.') matchdot++; while (! int_pending() && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && ! matchdot) continue; - if (patmatch(start, dp->d_name, 0)) { + if (patmatch(start, dp->d_name)) { namlen = dp->d_namlen; if (enddir + namlen + 1 > expdir_end) continue; memcpy(enddir, dp->d_name, namlen + 1); if (atend) appendarglist(arglist, stsavestr(expdir)); else { if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_DIR && dp->d_type != DT_LNK) continue; if (enddir + namlen + 2 > expdir_end) continue; enddir[namlen] = '/'; enddir[namlen + 1] = '\0'; expmeta(enddir + namlen + 1, endname, arglist); } } } closedir(dirp); if (! atend) endname[-esc - 1] = esc ? CTLESC : '/'; } static int expsortcmp(const void *p1, const void *p2) { const char *s1 = *(const char * const *)p1; const char *s2 = *(const char * const *)p2; return (strcmp(s1, s2)); } static wchar_t get_wc(const char **p) { wchar_t c; int chrlen; chrlen = mbtowc(&c, *p, 4); if (chrlen == 0) return 0; else if (chrlen == -1) c = 0; else *p += chrlen; return c; } /* * See if a character matches a character class, starting at the first colon * of "[:class:]". * If a valid character class is recognized, a pointer to the next character * after the final closing bracket is stored into *end, otherwise a null * pointer is stored into *end. */ static int match_charclass(const char *p, wchar_t chr, const char **end) { char name[20]; const char *nameend; wctype_t cclass; *end = NULL; p++; nameend = strstr(p, ":]"); if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || nameend == p) return 0; memcpy(name, p, nameend - p); name[nameend - p] = '\0'; *end = nameend + 2; cclass = wctype(name); /* An unknown class matches nothing but is valid nevertheless. */ if (cclass == 0) return 0; return iswctype(chr, cclass); } /* * Returns true if the pattern matches the string. */ static int -patmatch(const char *pattern, const char *string, int squoted) +patmatch(const char *pattern, const char *string) { const char *p, *q, *end; const char *bt_p, *bt_q; char c; wchar_t wc, wc2; p = pattern; q = string; bt_p = NULL; bt_q = NULL; for (;;) { switch (c = *p++) { case '\0': if (*q != '\0') goto backtrack; return 1; case CTLESC: - if (squoted && *q == CTLESC) - q++; if (*q++ != *p++) goto backtrack; break; - case CTLQUOTEMARK: - continue; case '?': - if (squoted && *q == CTLESC) - q++; if (*q == '\0') return 0; if (localeisutf8) { wc = get_wc(&q); /* * A '?' does not match invalid UTF-8 but a * '*' does, so backtrack. */ if (wc == 0) goto backtrack; } else wc = (unsigned char)*q++; break; case '*': c = *p; - while (c == CTLQUOTEMARK || c == '*') + while (c == '*') c = *++p; /* * If the pattern ends here, we know the string * matches without needing to look at the rest of it. */ if (c == '\0') return 1; /* * First try the shortest match for the '*' that * could work. We can forget any earlier '*' since * there is no way having it match more characters * can help us, given that we are already here. */ bt_p = p; bt_q = q; break; case '[': { const char *savep, *saveq; int invert, found; wchar_t chr; savep = p, saveq = q; invert = 0; if (*p == '!' || *p == '^') { invert++; p++; } found = 0; - if (squoted && *q == CTLESC) - q++; if (*q == '\0') return 0; if (localeisutf8) { chr = get_wc(&q); if (chr == 0) goto backtrack; } else chr = (unsigned char)*q++; c = *p++; do { if (c == '\0') { p = savep, q = saveq; c = '['; goto dft; } - if (c == CTLQUOTEMARK) - continue; if (c == '[' && *p == ':') { found |= match_charclass(p, chr, &end); if (end != NULL) p = end; } if (c == CTLESC) c = *p++; if (localeisutf8 && c & 0x80) { p--; wc = get_wc(&p); if (wc == 0) /* bad utf-8 */ return 0; } else wc = (unsigned char)c; if (*p == '-' && p[1] != ']') { p++; - while (*p == CTLQUOTEMARK) - p++; if (*p == CTLESC) p++; if (localeisutf8) { wc2 = get_wc(&p); if (wc2 == 0) /* bad utf-8 */ return 0; } else wc2 = (unsigned char)*p++; if ( collate_range_cmp(chr, wc) >= 0 && collate_range_cmp(chr, wc2) <= 0 ) found = 1; } else { if (chr == wc) found = 1; } } while ((c = *p++) != ']'); if (found == invert) goto backtrack; break; } dft: default: - if (squoted && *q == CTLESC) - q++; if (*q == '\0') return 0; if (*q++ == c) break; backtrack: /* * If we have a mismatch (other than hitting the end * of the string), go back to the last '*' seen and * have it match one additional character. */ if (bt_p == NULL) return 0; - if (squoted && *bt_q == CTLESC) - bt_q++; if (*bt_q == '\0') return 0; bt_q++; p = bt_p; q = bt_q; break; } } } /* * Remove any CTLESC and CTLQUOTEMARK characters from a string. */ void rmescapes(char *str) { char *p, *q; p = str; while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { if (*p++ == '\0') return; } q = p; while (*p) { if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { p++; continue; } if (*p == CTLESC) p++; *q++ = *p++; } *q = '\0'; } /* * See if a pattern matches in a case statement. */ int casematch(union node *pattern, const char *val) { struct stackmark smark; int result; char *p; setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); - ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, NULL); STPUTC('\0', expdest); p = grabstackstr(expdest); - result = patmatch(p, val, 0); + result = patmatch(p, val); popstackmark(&smark); return result; } /* * Our own itoa(). */ -static char * +static void cvtnum(int num, char *buf) { char temp[32]; int neg = num < 0; char *p = temp + 31; temp[31] = '\0'; do { *--p = num % 10 + '0'; } while ((num /= 10) != 0); if (neg) *--p = '-'; - STPUTS(p, buf); - return buf; + memcpy(buf, p, temp + 32 - p); } /* * Do most of the work for wordexp(3). */ int wordexpcmd(int argc, char **argv) { size_t len; int i; out1fmt("%08x", argc - 1); for (i = 1, len = 0; i < argc; i++) len += strlen(argv[i]); out1fmt("%08x", (int)len); for (i = 1; i < argc; i++) outbin(argv[i], strlen(argv[i]) + 1, out1); return (0); } /* * Do most of the work for wordexp(3), new version. */ int freebsd_wordexpcmd(int argc __unused, char **argv __unused) { struct arglist arglist; union node *args, *n; size_t len; int ch; int protected = 0; int fd = -1; int i; while ((ch = nextopt("f:p")) != '\0') { switch (ch) { case 'f': fd = number(shoptarg); break; case 'p': protected = 1; break; } } if (*argptr != NULL) error("wrong number of arguments"); if (fd < 0) error("missing fd"); INTOFF; setinputfd(fd, 1); INTON; args = parsewordexp(); popfile(); /* will also close fd */ if (protected) for (n = args; n != NULL; n = n->narg.next) { if (n->narg.backquote != NULL) { outcslow('C', out1); error("command substitution disabled"); } } outcslow(' ', out1); emptyarglist(&arglist); for (n = args; n != NULL; n = n->narg.next) expandarg(n, &arglist, EXP_FULL | EXP_TILDE); for (i = 0, len = 0; i < arglist.count; i++) len += strlen(arglist.args[i]); out1fmt("%016x %016zx", arglist.count, len); for (i = 0; i < arglist.count; i++) outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1); return (0); } Index: projects/clang380-import/bin/sh/expand.h =================================================================== --- projects/clang380-import/bin/sh/expand.h (revision 293015) +++ projects/clang380-import/bin/sh/expand.h (revision 293016) @@ -1,59 +1,62 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)expand.h 8.2 (Berkeley) 5/4/95 * $FreeBSD$ */ struct arglist { char **args; int count; int capacity; char *smallarg[1]; }; /* * expandarg() flags */ -#define EXP_FULL 0x1 /* perform word splitting & file globbing */ +#define EXP_SPLIT 0x1 /* perform word splitting */ #define EXP_TILDE 0x2 /* do normal tilde expansion */ #define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ #define EXP_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */ #define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */ +#define EXP_GLOB 0x80 /* perform file globbing */ + +#define EXP_FULL (EXP_SPLIT | EXP_GLOB) void emptyarglist(struct arglist *); void appendarglist(struct arglist *, char *); union node; void expandarg(union node *, struct arglist *, int); void rmescapes(char *); int casematch(union node *, const char *); Index: projects/clang380-import/contrib/binutils/bfd/elf32-arm.c =================================================================== --- projects/clang380-import/contrib/binutils/bfd/elf32-arm.c (revision 293015) +++ projects/clang380-import/contrib/binutils/bfd/elf32-arm.c (revision 293016) @@ -1,10477 +1,10491 @@ /* 32-bit ELF support for ARM Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include "bfd.h" #include "libiberty.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf-vxworks.h" #include "elf/arm.h" #ifndef NUM_ELEM #define NUM_ELEM(a) (sizeof (a) / (sizeof (a)[0])) #endif /* Return the relocation section associated with NAME. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define RELOC_SECTION(HTAB, NAME) \ ((HTAB)->use_rel ? ".rel" NAME : ".rela" NAME) /* Return size of a relocation entry. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define RELOC_SIZE(HTAB) \ ((HTAB)->use_rel \ ? sizeof (Elf32_External_Rel) \ : sizeof (Elf32_External_Rela)) /* Return function to swap relocations in. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define SWAP_RELOC_IN(HTAB) \ ((HTAB)->use_rel \ ? bfd_elf32_swap_reloc_in \ : bfd_elf32_swap_reloca_in) /* Return function to swap relocations out. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define SWAP_RELOC_OUT(HTAB) \ ((HTAB)->use_rel \ ? bfd_elf32_swap_reloc_out \ : bfd_elf32_swap_reloca_out) #define elf_info_to_howto 0 #define elf_info_to_howto_rel elf32_arm_info_to_howto #define ARM_ELF_ABI_VERSION 0 #ifdef __FreeBSD__ #define ARM_ELF_OS_ABI_VERSION ELFOSABI_FREEBSD #else #define ARM_ELF_OS_ABI_VERSION ELFOSABI_ARM #endif static struct elf_backend_data elf32_arm_vxworks_bed; /* Note: code such as elf32_arm_reloc_type_lookup expect to use e.g. R_ARM_PC24 as an index into this, and find the R_ARM_PC24 HOWTO in that slot. */ static reloc_howto_type elf32_arm_howto_table_1[] = { /* No relocation */ HOWTO (R_ARM_NONE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_NONE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_PC24, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PC24", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 32 bit absolute */ HOWTO (R_ARM_ABS32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* standard 32bit pc-relative reloc */ HOWTO (R_ARM_REL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_REL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 8 bit absolute - R_ARM_LDR_PC_G0 in AAELF */ HOWTO (R_ARM_LDR_PC_G0, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 16 bit absolute */ HOWTO (R_ARM_ABS16, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS16", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* 12 bit absolute */ HOWTO (R_ARM_ABS12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_ABS5, /* type */ 6, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 5, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_ABS5", /* name */ FALSE, /* partial_inplace */ 0x000007e0, /* src_mask */ 0x000007e0, /* dst_mask */ FALSE), /* pcrel_offset */ /* 8 bit absolute */ HOWTO (R_ARM_ABS8, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_SBREL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SBREL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_CALL, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 25, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_CALL", /* name */ FALSE, /* partial_inplace */ 0x07ff07ff, /* src_mask */ 0x07ff07ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_PC8, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_PC8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_BREL_ADJ, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_BREL_ADJ", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_SWI24, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SWI24", /* name */ FALSE, /* partial_inplace */ 0x00000000, /* src_mask */ 0x00000000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_SWI8, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SWI8", /* name */ FALSE, /* partial_inplace */ 0x00000000, /* src_mask */ 0x00000000, /* dst_mask */ FALSE), /* pcrel_offset */ /* BLX instruction for the ARM. */ HOWTO (R_ARM_XPC25, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 25, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_XPC25", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* BLX instruction for the Thumb. */ HOWTO (R_ARM_THM_XPC22, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 22, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_XPC22", /* name */ FALSE, /* partial_inplace */ 0x07ff07ff, /* src_mask */ 0x07ff07ff, /* dst_mask */ TRUE), /* pcrel_offset */ /* Dynamic TLS relocations. */ HOWTO (R_ARM_TLS_DTPMOD32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_DTPMOD32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_DTPOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_DTPOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_TPOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_TPOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Relocs used in ARM Linux */ HOWTO (R_ARM_COPY, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_COPY", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GLOB_DAT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GLOB_DAT", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_JUMP_SLOT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_JUMP_SLOT", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RELATIVE, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RELATIVE", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTPC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTPC", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_GOT32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_PLT32, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PLT32", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_CALL, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_CALL", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_JUMP24, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_JUMP24", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP24, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP24", /* name */ FALSE, /* partial_inplace */ 0x07ff2fff, /* src_mask */ 0x07ff2fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_BASE_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_BASE_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL7_0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_7_0", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL15_8, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 8, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_15_8",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL23_15, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 16, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_23_15",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SBREL_11_0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SBREL_11_0",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SBREL_19_12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 12, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SBREL_19_12",/* name */ FALSE, /* partial_inplace */ 0x000ff000, /* src_mask */ 0x000ff000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SBREL_27_20, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 20, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SBREL_27_20",/* name */ FALSE, /* partial_inplace */ 0x0ff00000, /* src_mask */ 0x0ff00000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TARGET1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TARGET1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ROSEGREL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ROSEGREL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_V4BX, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_V4BX", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TARGET2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TARGET2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_PREL31, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 31, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PREL31", /* name */ FALSE, /* partial_inplace */ 0x7fffffff, /* src_mask */ 0x7fffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_ABS_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_ABS_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_ABS", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_PREL_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_PREL_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_PREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_ABS_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_ABS_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_ABS", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_PREL_NC,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_PREL_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_PREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP19, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 19, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP19", /* name */ FALSE, /* partial_inplace */ 0x043f2fff, /* src_mask */ 0x043f2fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP6, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 6, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP6", /* name */ FALSE, /* partial_inplace */ 0x02f8, /* src_mask */ 0x02f8, /* dst_mask */ TRUE), /* pcrel_offset */ /* These are declared as 13-bit signed relocations because we can address -4095 .. 4095(base) by altering ADDW to SUBW or vice versa. */ HOWTO (R_ARM_THM_ALU_PREL_11_0,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 13, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_ALU_PREL_11_0",/* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_PC12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 13, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_PC12", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ABS32_NOI, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS32_NOI", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_REL32_NOI, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_REL32_NOI", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Group relocations. */ HOWTO (R_ARM_ALU_PC_G0_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G0_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G1_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G1_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G0_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G0_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G1_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G1_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* End of group relocations. */ HOWTO (R_ARM_MOVW_BREL_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_BREL_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_BREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_BREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_BREL_NC,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_BREL_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_BREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_BREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ EMPTY_HOWTO (90), /* unallocated */ EMPTY_HOWTO (91), EMPTY_HOWTO (92), EMPTY_HOWTO (93), HOWTO (R_ARM_PLT32_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PLT32_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_PREL", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_GOT_BREL12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_BREL12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTOFF12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTOFF12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ EMPTY_HOWTO (R_ARM_GOTRELAX), /* reserved for future GOT-load optimizations */ /* GNU extension to record C++ vtable member usage */ HOWTO (R_ARM_GNU_VTENTRY, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ _bfd_elf_rel_vtable_reloc_fn, /* special_function */ "R_ARM_GNU_VTENTRY", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ /* GNU extension to record C++ vtable hierarchy */ HOWTO (R_ARM_GNU_VTINHERIT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ NULL, /* special_function */ "R_ARM_GNU_VTINHERIT", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP11, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 11, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP11", /* name */ FALSE, /* partial_inplace */ 0x000007ff, /* src_mask */ 0x000007ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP8, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ TRUE), /* pcrel_offset */ /* TLS relocations */ HOWTO (R_ARM_TLS_GD32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ NULL, /* special_function */ "R_ARM_TLS_GD32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDM32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDM32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDO32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDO32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_IE32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ NULL, /* special_function */ "R_ARM_TLS_IE32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LE32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LE32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDO12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDO12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LE12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LE12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_IE12GP, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_IE12GP", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ }; /* 112-127 private relocations 128 R_ARM_ME_TOO, obsolete 129-255 unallocated in AAELF. 249-255 extended, currently unused, relocations: */ static reloc_howto_type elf32_arm_howto_table_2[4] = { HOWTO (R_ARM_RREL32, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RREL32", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RABS32, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RABS32", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RPC24, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RPC24", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RBASE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RBASE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE) /* pcrel_offset */ }; static reloc_howto_type * elf32_arm_howto_from_type (unsigned int r_type) { if (r_type < NUM_ELEM (elf32_arm_howto_table_1)) return &elf32_arm_howto_table_1[r_type]; if (r_type >= R_ARM_RREL32 && r_type < R_ARM_RREL32 + NUM_ELEM (elf32_arm_howto_table_2)) return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32]; return NULL; } static void elf32_arm_info_to_howto (bfd * abfd ATTRIBUTE_UNUSED, arelent * bfd_reloc, Elf_Internal_Rela * elf_reloc) { unsigned int r_type; r_type = ELF32_R_TYPE (elf_reloc->r_info); bfd_reloc->howto = elf32_arm_howto_from_type (r_type); } struct elf32_arm_reloc_map { bfd_reloc_code_real_type bfd_reloc_val; unsigned char elf_reloc_val; }; /* All entries in this list must also be present in elf32_arm_howto_table. */ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] = { {BFD_RELOC_NONE, R_ARM_NONE}, {BFD_RELOC_ARM_PCREL_BRANCH, R_ARM_PC24}, {BFD_RELOC_ARM_PCREL_CALL, R_ARM_CALL}, {BFD_RELOC_ARM_PCREL_JUMP, R_ARM_JUMP24}, {BFD_RELOC_ARM_PCREL_BLX, R_ARM_XPC25}, {BFD_RELOC_THUMB_PCREL_BLX, R_ARM_THM_XPC22}, {BFD_RELOC_32, R_ARM_ABS32}, {BFD_RELOC_32_PCREL, R_ARM_REL32}, {BFD_RELOC_8, R_ARM_ABS8}, {BFD_RELOC_16, R_ARM_ABS16}, {BFD_RELOC_ARM_OFFSET_IMM, R_ARM_ABS12}, {BFD_RELOC_ARM_THUMB_OFFSET, R_ARM_THM_ABS5}, {BFD_RELOC_THUMB_PCREL_BRANCH25, R_ARM_THM_JUMP24}, {BFD_RELOC_THUMB_PCREL_BRANCH23, R_ARM_THM_CALL}, {BFD_RELOC_THUMB_PCREL_BRANCH12, R_ARM_THM_JUMP11}, {BFD_RELOC_THUMB_PCREL_BRANCH20, R_ARM_THM_JUMP19}, {BFD_RELOC_THUMB_PCREL_BRANCH9, R_ARM_THM_JUMP8}, {BFD_RELOC_THUMB_PCREL_BRANCH7, R_ARM_THM_JUMP6}, {BFD_RELOC_ARM_GLOB_DAT, R_ARM_GLOB_DAT}, {BFD_RELOC_ARM_JUMP_SLOT, R_ARM_JUMP_SLOT}, {BFD_RELOC_ARM_RELATIVE, R_ARM_RELATIVE}, {BFD_RELOC_ARM_GOTOFF, R_ARM_GOTOFF32}, {BFD_RELOC_ARM_GOTPC, R_ARM_GOTPC}, {BFD_RELOC_ARM_GOT32, R_ARM_GOT32}, {BFD_RELOC_ARM_PLT32, R_ARM_PLT32}, {BFD_RELOC_ARM_TARGET1, R_ARM_TARGET1}, {BFD_RELOC_ARM_ROSEGREL32, R_ARM_ROSEGREL32}, {BFD_RELOC_ARM_SBREL32, R_ARM_SBREL32}, {BFD_RELOC_ARM_PREL31, R_ARM_PREL31}, {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2}, {BFD_RELOC_ARM_PLT32, R_ARM_PLT32}, {BFD_RELOC_ARM_TLS_GD32, R_ARM_TLS_GD32}, {BFD_RELOC_ARM_TLS_LDO32, R_ARM_TLS_LDO32}, {BFD_RELOC_ARM_TLS_LDM32, R_ARM_TLS_LDM32}, {BFD_RELOC_ARM_TLS_DTPMOD32, R_ARM_TLS_DTPMOD32}, {BFD_RELOC_ARM_TLS_DTPOFF32, R_ARM_TLS_DTPOFF32}, {BFD_RELOC_ARM_TLS_TPOFF32, R_ARM_TLS_TPOFF32}, {BFD_RELOC_ARM_TLS_IE32, R_ARM_TLS_IE32}, {BFD_RELOC_ARM_TLS_LE32, R_ARM_TLS_LE32}, {BFD_RELOC_VTABLE_INHERIT, R_ARM_GNU_VTINHERIT}, {BFD_RELOC_VTABLE_ENTRY, R_ARM_GNU_VTENTRY}, {BFD_RELOC_ARM_MOVW, R_ARM_MOVW_ABS_NC}, {BFD_RELOC_ARM_MOVT, R_ARM_MOVT_ABS}, {BFD_RELOC_ARM_MOVW_PCREL, R_ARM_MOVW_PREL_NC}, {BFD_RELOC_ARM_MOVT_PCREL, R_ARM_MOVT_PREL}, {BFD_RELOC_ARM_THUMB_MOVW, R_ARM_THM_MOVW_ABS_NC}, {BFD_RELOC_ARM_THUMB_MOVT, R_ARM_THM_MOVT_ABS}, {BFD_RELOC_ARM_THUMB_MOVW_PCREL, R_ARM_THM_MOVW_PREL_NC}, {BFD_RELOC_ARM_THUMB_MOVT_PCREL, R_ARM_THM_MOVT_PREL}, {BFD_RELOC_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G0_NC}, {BFD_RELOC_ARM_ALU_PC_G0, R_ARM_ALU_PC_G0}, {BFD_RELOC_ARM_ALU_PC_G1_NC, R_ARM_ALU_PC_G1_NC}, {BFD_RELOC_ARM_ALU_PC_G1, R_ARM_ALU_PC_G1}, {BFD_RELOC_ARM_ALU_PC_G2, R_ARM_ALU_PC_G2}, {BFD_RELOC_ARM_LDR_PC_G0, R_ARM_LDR_PC_G0}, {BFD_RELOC_ARM_LDR_PC_G1, R_ARM_LDR_PC_G1}, {BFD_RELOC_ARM_LDR_PC_G2, R_ARM_LDR_PC_G2}, {BFD_RELOC_ARM_LDRS_PC_G0, R_ARM_LDRS_PC_G0}, {BFD_RELOC_ARM_LDRS_PC_G1, R_ARM_LDRS_PC_G1}, {BFD_RELOC_ARM_LDRS_PC_G2, R_ARM_LDRS_PC_G2}, {BFD_RELOC_ARM_LDC_PC_G0, R_ARM_LDC_PC_G0}, {BFD_RELOC_ARM_LDC_PC_G1, R_ARM_LDC_PC_G1}, {BFD_RELOC_ARM_LDC_PC_G2, R_ARM_LDC_PC_G2}, {BFD_RELOC_ARM_ALU_SB_G0_NC, R_ARM_ALU_SB_G0_NC}, {BFD_RELOC_ARM_ALU_SB_G0, R_ARM_ALU_SB_G0}, {BFD_RELOC_ARM_ALU_SB_G1_NC, R_ARM_ALU_SB_G1_NC}, {BFD_RELOC_ARM_ALU_SB_G1, R_ARM_ALU_SB_G1}, {BFD_RELOC_ARM_ALU_SB_G2, R_ARM_ALU_SB_G2}, {BFD_RELOC_ARM_LDR_SB_G0, R_ARM_LDR_SB_G0}, {BFD_RELOC_ARM_LDR_SB_G1, R_ARM_LDR_SB_G1}, {BFD_RELOC_ARM_LDR_SB_G2, R_ARM_LDR_SB_G2}, {BFD_RELOC_ARM_LDRS_SB_G0, R_ARM_LDRS_SB_G0}, {BFD_RELOC_ARM_LDRS_SB_G1, R_ARM_LDRS_SB_G1}, {BFD_RELOC_ARM_LDRS_SB_G2, R_ARM_LDRS_SB_G2}, {BFD_RELOC_ARM_LDC_SB_G0, R_ARM_LDC_SB_G0}, {BFD_RELOC_ARM_LDC_SB_G1, R_ARM_LDC_SB_G1}, {BFD_RELOC_ARM_LDC_SB_G2, R_ARM_LDC_SB_G2} }; static reloc_howto_type * elf32_arm_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { unsigned int i; for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++) if (elf32_arm_reloc_map[i].bfd_reloc_val == code) return elf32_arm_howto_from_type (elf32_arm_reloc_map[i].elf_reloc_val); return NULL; } static reloc_howto_type * elf32_arm_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) { unsigned int i; for (i = 0; i < (sizeof (elf32_arm_howto_table_1) / sizeof (elf32_arm_howto_table_1[0])); i++) if (elf32_arm_howto_table_1[i].name != NULL && strcasecmp (elf32_arm_howto_table_1[i].name, r_name) == 0) return &elf32_arm_howto_table_1[i]; for (i = 0; i < (sizeof (elf32_arm_howto_table_2) / sizeof (elf32_arm_howto_table_2[0])); i++) if (elf32_arm_howto_table_2[i].name != NULL && strcasecmp (elf32_arm_howto_table_2[i].name, r_name) == 0) return &elf32_arm_howto_table_2[i]; return NULL; } /* Support for core dump NOTE sections */ static bfd_boolean elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) { int offset; size_t size; switch (note->descsz) { default: return FALSE; case 148: /* Linux/ARM 32-bit*/ /* pr_cursig */ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); /* pr_pid */ elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24); /* pr_reg */ offset = 72; size = 72; break; case 96: /* FreeBSD/ARM */ /* pr_cursig */ if (elf_tdata(abfd)->core_signal == 0) elf_tdata (abfd)->core_signal = ((int *)(note->descdata))[5]; /* pr_pid */ elf_tdata (abfd)->core_pid = ((int *)(note->descdata))[6]; /* pr_reg */ offset = 28; size = 68; break; } /* Make a ".reg/999" section. */ return _bfd_elfcore_make_pseudosection (abfd, ".reg", size, note->descpos + offset); } static bfd_boolean elf32_arm_nabi_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) { switch (note->descsz) { default: return FALSE; case 124: /* Linux/ARM elf_prpsinfo */ elf_tdata (abfd)->core_program = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16); elf_tdata (abfd)->core_command = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80); } /* Note that for some reason, a spurious space is tacked onto the end of the args in some (at least one anyway) implementations, so strip it off if it exists. */ { char *command = elf_tdata (abfd)->core_command; int n = strlen (command); if (0 < n && command[n - 1] == ' ') command[n - 1] = '\0'; } return TRUE; } #define TARGET_LITTLE_SYM bfd_elf32_littlearm_vec #define TARGET_LITTLE_NAME "elf32-littlearm" #define TARGET_BIG_SYM bfd_elf32_bigarm_vec #define TARGET_BIG_NAME "elf32-bigarm" #define elf_backend_grok_prstatus elf32_arm_nabi_grok_prstatus #define elf_backend_grok_psinfo elf32_arm_nabi_grok_psinfo typedef unsigned long int insn32; typedef unsigned short int insn16; /* In lieu of proper flags, assume all EABIv4 or later objects are interworkable. */ #define INTERWORK_FLAG(abfd) \ (EF_ARM_EABI_VERSION (elf_elfheader (abfd)->e_flags) >= EF_ARM_EABI_VER4 \ || (elf_elfheader (abfd)->e_flags & EF_ARM_INTERWORK)) /* The linker script knows the section names for placement. The entry_names are used to do simple name mangling on the stubs. Given a function name, and its type, the stub can be found. The name can be changed. The only requirement is the %s be present. */ #define THUMB2ARM_GLUE_SECTION_NAME ".glue_7t" #define THUMB2ARM_GLUE_ENTRY_NAME "__%s_from_thumb" #define ARM2THUMB_GLUE_SECTION_NAME ".glue_7" #define ARM2THUMB_GLUE_ENTRY_NAME "__%s_from_arm" #define VFP11_ERRATUM_VENEER_SECTION_NAME ".vfp11_veneer" #define VFP11_ERRATUM_VENEER_ENTRY_NAME "__vfp11_veneer_%x" /* The name of the dynamic interpreter. This is put in the .interp section. */ #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" #ifdef FOUR_WORD_PLT /* The first entry in a procedure linkage table looks like this. It is set up so that any shared library function that is called before the relocation has been set up calls the dynamic linker first. */ static const bfd_vma elf32_arm_plt0_entry [] = { 0xe52de004, /* str lr, [sp, #-4]! */ 0xe59fe010, /* ldr lr, [pc, #16] */ 0xe08fe00e, /* add lr, pc, lr */ 0xe5bef008, /* ldr pc, [lr, #8]! */ }; /* Subsequent entries in a procedure linkage table look like this. */ static const bfd_vma elf32_arm_plt_entry [] = { 0xe28fc600, /* add ip, pc, #NN */ 0xe28cca00, /* add ip, ip, #NN */ 0xe5bcf000, /* ldr pc, [ip, #NN]! */ 0x00000000, /* unused */ }; #else /* The first entry in a procedure linkage table looks like this. It is set up so that any shared library function that is called before the relocation has been set up calls the dynamic linker first. */ static const bfd_vma elf32_arm_plt0_entry [] = { 0xe52de004, /* str lr, [sp, #-4]! */ 0xe59fe004, /* ldr lr, [pc, #4] */ 0xe08fe00e, /* add lr, pc, lr */ 0xe5bef008, /* ldr pc, [lr, #8]! */ 0x00000000, /* &GOT[0] - . */ }; /* Subsequent entries in a procedure linkage table look like this. */ static const bfd_vma elf32_arm_plt_entry [] = { 0xe28fc600, /* add ip, pc, #0xNN00000 */ 0xe28cca00, /* add ip, ip, #0xNN000 */ 0xe5bcf000, /* ldr pc, [ip, #0xNNN]! */ }; #endif /* The format of the first entry in the procedure linkage table for a VxWorks executable. */ static const bfd_vma elf32_arm_vxworks_exec_plt0_entry[] = { 0xe52dc008, /* str ip,[sp,#-8]! */ 0xe59fc000, /* ldr ip,[pc] */ 0xe59cf008, /* ldr pc,[ip,#8] */ 0x00000000, /* .long _GLOBAL_OFFSET_TABLE_ */ }; /* The format of subsequent entries in a VxWorks executable. */ static const bfd_vma elf32_arm_vxworks_exec_plt_entry[] = { 0xe59fc000, /* ldr ip,[pc] */ 0xe59cf000, /* ldr pc,[ip] */ 0x00000000, /* .long @got */ 0xe59fc000, /* ldr ip,[pc] */ 0xea000000, /* b _PLT */ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */ }; /* The format of entries in a VxWorks shared library. */ static const bfd_vma elf32_arm_vxworks_shared_plt_entry[] = { 0xe59fc000, /* ldr ip,[pc] */ 0xe79cf009, /* ldr pc,[ip,r9] */ 0x00000000, /* .long @got */ 0xe59fc000, /* ldr ip,[pc] */ 0xe599f008, /* ldr pc,[r9,#8] */ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */ }; /* An initial stub used if the PLT entry is referenced from Thumb code. */ #define PLT_THUMB_STUB_SIZE 4 static const bfd_vma elf32_arm_plt_thumb_stub [] = { 0x4778, /* bx pc */ 0x46c0 /* nop */ }; /* The entries in a PLT when using a DLL-based target with multiple address spaces. */ static const bfd_vma elf32_arm_symbian_plt_entry [] = { 0xe51ff004, /* ldr pc, [pc, #-4] */ 0x00000000, /* dcd R_ARM_GLOB_DAT(X) */ }; /* Used to build a map of a section. This is required for mixed-endian code/data. */ typedef struct elf32_elf_section_map { bfd_vma vma; char type; } elf32_arm_section_map; /* Information about a VFP11 erratum veneer, or a branch to such a veneer. */ typedef enum { VFP11_ERRATUM_BRANCH_TO_ARM_VENEER, VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER, VFP11_ERRATUM_ARM_VENEER, VFP11_ERRATUM_THUMB_VENEER } elf32_vfp11_erratum_type; typedef struct elf32_vfp11_erratum_list { struct elf32_vfp11_erratum_list *next; bfd_vma vma; union { struct { struct elf32_vfp11_erratum_list *veneer; unsigned int vfp_insn; } b; struct { struct elf32_vfp11_erratum_list *branch; unsigned int id; } v; } u; elf32_vfp11_erratum_type type; } elf32_vfp11_erratum_list; typedef struct _arm_elf_section_data { struct bfd_elf_section_data elf; unsigned int mapcount; unsigned int mapsize; elf32_arm_section_map *map; unsigned int erratumcount; elf32_vfp11_erratum_list *erratumlist; } _arm_elf_section_data; #define elf32_arm_section_data(sec) \ ((_arm_elf_section_data *) elf_section_data (sec)) /* The size of the thread control block. */ #define TCB_SIZE 8 struct elf32_arm_obj_tdata { struct elf_obj_tdata root; /* tls_type for each local got entry. */ char *local_got_tls_type; /* Zero to warn when linking objects with incompatible enum sizes. */ int no_enum_size_warning; }; #define elf32_arm_tdata(abfd) \ ((struct elf32_arm_obj_tdata *) (abfd)->tdata.any) #define elf32_arm_local_got_tls_type(abfd) \ (elf32_arm_tdata (abfd)->local_got_tls_type) static bfd_boolean elf32_arm_mkobject (bfd *abfd) { if (abfd->tdata.any == NULL) { bfd_size_type amt = sizeof (struct elf32_arm_obj_tdata); abfd->tdata.any = bfd_zalloc (abfd, amt); if (abfd->tdata.any == NULL) return FALSE; } return bfd_elf_mkobject (abfd); } /* The ARM linker needs to keep track of the number of relocs that it decides to copy in check_relocs for each symbol. This is so that it can discard PC relative relocs if it doesn't need them when linking with -Bsymbolic. We store the information in a field extending the regular ELF linker hash table. */ /* This structure keeps track of the number of relocs we have copied for a given symbol. */ struct elf32_arm_relocs_copied { /* Next section. */ struct elf32_arm_relocs_copied * next; /* A section in dynobj. */ asection * section; /* Number of relocs copied in this section. */ bfd_size_type count; /* Number of PC-relative relocs copied in this section. */ bfd_size_type pc_count; }; #define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent)) /* Arm ELF linker hash entry. */ struct elf32_arm_link_hash_entry { struct elf_link_hash_entry root; /* Number of PC relative relocs copied for this symbol. */ struct elf32_arm_relocs_copied * relocs_copied; /* We reference count Thumb references to a PLT entry separately, so that we can emit the Thumb trampoline only if needed. */ bfd_signed_vma plt_thumb_refcount; /* Since PLT entries have variable size if the Thumb prologue is used, we need to record the index into .got.plt instead of recomputing it from the PLT offset. */ bfd_signed_vma plt_got_offset; #define GOT_UNKNOWN 0 #define GOT_NORMAL 1 #define GOT_TLS_GD 2 #define GOT_TLS_IE 4 unsigned char tls_type; /* The symbol marking the real symbol location for exported thumb symbols with Arm stubs. */ struct elf_link_hash_entry *export_glue; }; /* Traverse an arm ELF linker hash table. */ #define elf32_arm_link_hash_traverse(table, func, info) \ (elf_link_hash_traverse \ (&(table)->root, \ (bfd_boolean (*) (struct elf_link_hash_entry *, void *)) (func), \ (info))) /* Get the ARM elf linker hash table from a link_info structure. */ #define elf32_arm_hash_table(info) \ ((struct elf32_arm_link_hash_table *) ((info)->hash)) /* ARM ELF linker hash table. */ struct elf32_arm_link_hash_table { /* The main hash table. */ struct elf_link_hash_table root; /* The size in bytes of the section containing the Thumb-to-ARM glue. */ bfd_size_type thumb_glue_size; /* The size in bytes of the section containing the ARM-to-Thumb glue. */ bfd_size_type arm_glue_size; /* The size in bytes of the section containing glue for VFP11 erratum veneers. */ bfd_size_type vfp11_erratum_glue_size; /* An arbitrary input BFD chosen to hold the glue sections. */ bfd * bfd_of_glue_owner; /* Nonzero to output a BE8 image. */ int byteswap_code; /* Zero if R_ARM_TARGET1 means R_ARM_ABS32. Nonzero if R_ARM_TARGET1 means R_ARM_REL32. */ int target1_is_rel; /* The relocation to use for R_ARM_TARGET2 relocations. */ int target2_reloc; /* Nonzero to fix BX instructions for ARMv4 targets. */ int fix_v4bx; /* Nonzero if the ARM/Thumb BLX instructions are available for use. */ int use_blx; /* What sort of code sequences we should look for which may trigger the VFP11 denorm erratum. */ bfd_arm_vfp11_fix vfp11_fix; /* Global counter for the number of fixes we have emitted. */ int num_vfp11_fixes; /* Nonzero to force PIC branch veneers. */ int pic_veneer; /* The number of bytes in the initial entry in the PLT. */ bfd_size_type plt_header_size; /* The number of bytes in the subsequent PLT etries. */ bfd_size_type plt_entry_size; /* True if the target system is VxWorks. */ int vxworks_p; /* True if the target system is Symbian OS. */ int symbian_p; /* True if the target uses REL relocations. */ int use_rel; /* Short-cuts to get to dynamic linker sections. */ asection *sgot; asection *sgotplt; asection *srelgot; asection *splt; asection *srelplt; asection *sdynbss; asection *srelbss; /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */ asection *srelplt2; /* Data for R_ARM_TLS_LDM32 relocations. */ union { bfd_signed_vma refcount; bfd_vma offset; } tls_ldm_got; /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; /* For convenience in allocate_dynrelocs. */ bfd * obfd; }; /* Create an entry in an ARM ELF linker hash table. */ static struct bfd_hash_entry * elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry, struct bfd_hash_table * table, const char * string) { struct elf32_arm_link_hash_entry * ret = (struct elf32_arm_link_hash_entry *) entry; /* Allocate the structure if it has not already been allocated by a subclass. */ if (ret == (struct elf32_arm_link_hash_entry *) NULL) ret = bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry)); if (ret == NULL) return (struct bfd_hash_entry *) ret; /* Call the allocation method of the superclass. */ ret = ((struct elf32_arm_link_hash_entry *) _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); if (ret != NULL) { ret->relocs_copied = NULL; ret->tls_type = GOT_UNKNOWN; ret->plt_thumb_refcount = 0; ret->plt_got_offset = -1; ret->export_glue = NULL; } return (struct bfd_hash_entry *) ret; } /* Return true if NAME is the name of the relocation section associated with S. */ static bfd_boolean reloc_section_p (struct elf32_arm_link_hash_table *htab, const char *name, asection *s) { if (htab->use_rel) return CONST_STRNEQ (name, ".rel") && strcmp (s->name, name + 4) == 0; else return CONST_STRNEQ (name, ".rela") && strcmp (s->name, name + 5) == 0; } /* Create .got, .gotplt, and .rel(a).got sections in DYNOBJ, and set up shortcuts to them in our hash table. */ static bfd_boolean create_got_section (bfd *dynobj, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); /* BPABI objects never have a GOT, or associated sections. */ if (htab->symbian_p) return TRUE; if (! _bfd_elf_create_got_section (dynobj, info)) return FALSE; htab->sgot = bfd_get_section_by_name (dynobj, ".got"); htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); if (!htab->sgot || !htab->sgotplt) abort (); htab->srelgot = bfd_make_section_with_flags (dynobj, RELOC_SECTION (htab, ".got"), (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED | SEC_READONLY)); if (htab->srelgot == NULL || ! bfd_set_section_alignment (dynobj, htab->srelgot, 2)) return FALSE; return TRUE; } /* Create .plt, .rel(a).plt, .got, .got.plt, .rel(a).got, .dynbss, and .rel(a).bss sections in DYNOBJ, and set up shortcuts to them in our hash table. */ static bfd_boolean elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); if (!htab->sgot && !create_got_section (dynobj, info)) return FALSE; if (!_bfd_elf_create_dynamic_sections (dynobj, info)) return FALSE; htab->splt = bfd_get_section_by_name (dynobj, ".plt"); htab->srelplt = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt")); htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss"); if (!info->shared) htab->srelbss = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".bss")); if (htab->vxworks_p) { if (!elf_vxworks_create_dynamic_sections (dynobj, info, &htab->srelplt2)) return FALSE; if (info->shared) { htab->plt_header_size = 0; htab->plt_entry_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_shared_plt_entry); } else { htab->plt_header_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt0_entry); htab->plt_entry_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt_entry); } } if (!htab->splt || !htab->srelplt || !htab->sdynbss || (!info->shared && !htab->srelbss)) abort (); return TRUE; } /* Copy the extra info we tack onto an elf_link_hash_entry. */ static void elf32_arm_copy_indirect_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *dir, struct elf_link_hash_entry *ind) { struct elf32_arm_link_hash_entry *edir, *eind; edir = (struct elf32_arm_link_hash_entry *) dir; eind = (struct elf32_arm_link_hash_entry *) ind; if (eind->relocs_copied != NULL) { if (edir->relocs_copied != NULL) { struct elf32_arm_relocs_copied **pp; struct elf32_arm_relocs_copied *p; /* Add reloc counts against the indirect sym to the direct sym list. Merge any entries against the same section. */ for (pp = &eind->relocs_copied; (p = *pp) != NULL; ) { struct elf32_arm_relocs_copied *q; for (q = edir->relocs_copied; q != NULL; q = q->next) if (q->section == p->section) { q->pc_count += p->pc_count; q->count += p->count; *pp = p->next; break; } if (q == NULL) pp = &p->next; } *pp = edir->relocs_copied; } edir->relocs_copied = eind->relocs_copied; eind->relocs_copied = NULL; } if (ind->root.type == bfd_link_hash_indirect) { /* Copy over PLT info. */ edir->plt_thumb_refcount += eind->plt_thumb_refcount; eind->plt_thumb_refcount = 0; if (dir->got.refcount <= 0) { edir->tls_type = eind->tls_type; eind->tls_type = GOT_UNKNOWN; } } _bfd_elf_link_hash_copy_indirect (info, dir, ind); } /* Create an ARM elf linker hash table. */ static struct bfd_link_hash_table * elf32_arm_link_hash_table_create (bfd *abfd) { struct elf32_arm_link_hash_table *ret; bfd_size_type amt = sizeof (struct elf32_arm_link_hash_table); ret = bfd_malloc (amt); if (ret == NULL) return NULL; if (!_bfd_elf_link_hash_table_init (& ret->root, abfd, elf32_arm_link_hash_newfunc, sizeof (struct elf32_arm_link_hash_entry))) { free (ret); return NULL; } ret->sgot = NULL; ret->sgotplt = NULL; ret->srelgot = NULL; ret->splt = NULL; ret->srelplt = NULL; ret->sdynbss = NULL; ret->srelbss = NULL; ret->srelplt2 = NULL; ret->thumb_glue_size = 0; ret->arm_glue_size = 0; ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; ret->vfp11_erratum_glue_size = 0; ret->num_vfp11_fixes = 0; ret->bfd_of_glue_owner = NULL; ret->byteswap_code = 0; ret->target1_is_rel = 0; ret->target2_reloc = R_ARM_NONE; #ifdef FOUR_WORD_PLT ret->plt_header_size = 16; ret->plt_entry_size = 16; #else ret->plt_header_size = 20; ret->plt_entry_size = 12; #endif ret->fix_v4bx = 0; ret->use_blx = 0; ret->vxworks_p = 0; ret->symbian_p = 0; ret->use_rel = 1; ret->sym_sec.abfd = NULL; ret->obfd = abfd; ret->tls_ldm_got.refcount = 0; return &ret->root.root; } /* Locate the Thumb encoded calling stub for NAME. */ static struct elf_link_hash_entry * find_thumb_glue (struct bfd_link_info *link_info, const char *name, char **error_message) { char *tmp_name; struct elf_link_hash_entry *hash; struct elf32_arm_link_hash_table *hash_table; /* We need a pointer to the armelf specific hash table. */ hash_table = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name); hash = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (hash == NULL) asprintf (error_message, _("unable to find THUMB glue '%s' for '%s'"), tmp_name, name); free (tmp_name); return hash; } /* Locate the ARM encoded calling stub for NAME. */ static struct elf_link_hash_entry * find_arm_glue (struct bfd_link_info *link_info, const char *name, char **error_message) { char *tmp_name; struct elf_link_hash_entry *myh; struct elf32_arm_link_hash_table *hash_table; /* We need a pointer to the elfarm specific hash table. */ hash_table = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) asprintf (error_message, _("unable to find ARM glue '%s' for '%s'"), tmp_name, name); free (tmp_name); return myh; } /* ARM->Thumb glue (static images): .arm __func_from_arm: ldr r12, __func_addr bx r12 __func_addr: .word func @ behave as if you saw a ARM_32 reloc. (v5t static images) .arm __func_from_arm: ldr pc, __func_addr __func_addr: .word func @ behave as if you saw a ARM_32 reloc. (relocatable images) .arm __func_from_arm: ldr r12, __func_offset add r12, r12, pc bx r12 __func_offset: .word func - . */ #define ARM2THUMB_STATIC_GLUE_SIZE 12 static const insn32 a2t1_ldr_insn = 0xe59fc000; static const insn32 a2t2_bx_r12_insn = 0xe12fff1c; static const insn32 a2t3_func_addr_insn = 0x00000001; #define ARM2THUMB_V5_STATIC_GLUE_SIZE 8 static const insn32 a2t1v5_ldr_insn = 0xe51ff004; static const insn32 a2t2v5_func_addr_insn = 0x00000001; #define ARM2THUMB_PIC_GLUE_SIZE 16 static const insn32 a2t1p_ldr_insn = 0xe59fc004; static const insn32 a2t2p_add_pc_insn = 0xe08cc00f; static const insn32 a2t3p_bx_r12_insn = 0xe12fff1c; /* Thumb->ARM: Thumb->(non-interworking aware) ARM .thumb .thumb .align 2 .align 2 __func_from_thumb: __func_from_thumb: bx pc push {r6, lr} nop ldr r6, __func_addr .arm mov lr, pc __func_change_to_arm: bx r6 b func .arm __func_back_to_thumb: ldmia r13! {r6, lr} bx lr __func_addr: .word func */ #define THUMB2ARM_GLUE_SIZE 8 static const insn16 t2a1_bx_pc_insn = 0x4778; static const insn16 t2a2_noop_insn = 0x46c0; static const insn32 t2a3_b_insn = 0xea000000; #define VFP11_ERRATUM_VENEER_SIZE 8 #ifndef ELFARM_NABI_C_INCLUDED bfd_boolean bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info) { asection * s; bfd_byte * foo; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); if (globals->arm_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->arm_glue_size); BFD_ASSERT (s->size == globals->arm_glue_size); s->contents = foo; } if (globals->thumb_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->thumb_glue_size); BFD_ASSERT (s->size == globals->thumb_glue_size); s->contents = foo; } if (globals->vfp11_erratum_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->vfp11_erratum_glue_size); BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size); s->contents = foo; } return TRUE; } /* Allocate space and symbols for calling a Thumb function from Arm mode. returns the symbol identifying teh stub. */ static struct elf_link_hash_entry * record_arm_to_thumb_glue (struct bfd_link_info * link_info, struct elf_link_hash_entry * h) { const char * name = h->root.root.string; asection * s; char * tmp_name; struct elf_link_hash_entry * myh; struct bfd_link_hash_entry * bh; struct elf32_arm_link_hash_table * globals; bfd_vma val; bfd_size_type size; globals = elf32_arm_hash_table (link_info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh != NULL) { /* We've already seen this guy. */ free (tmp_name); return myh; } /* The only trick here is using hash_table->arm_glue_size as the value. Even though the section isn't allocated yet, this is where we will be putting it. */ bh = NULL; val = globals->arm_glue_size + 1; _bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner, tmp_name, BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; free (tmp_name); if (link_info->shared || globals->root.is_relocatable_executable || globals->pic_veneer) size = ARM2THUMB_PIC_GLUE_SIZE; else if (globals->use_blx) size = ARM2THUMB_V5_STATIC_GLUE_SIZE; else size = ARM2THUMB_STATIC_GLUE_SIZE; s->size += size; globals->arm_glue_size += size; return myh; } static void record_thumb_to_arm_glue (struct bfd_link_info *link_info, struct elf_link_hash_entry *h) { const char *name = h->root.root.string; asection *s; char *tmp_name; struct elf_link_hash_entry *myh; struct bfd_link_hash_entry *bh; struct elf32_arm_link_hash_table *hash_table; bfd_vma val; hash_table = elf32_arm_hash_table (link_info); BFD_ASSERT (hash_table != NULL); BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (hash_table->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (myh != NULL) { /* We've already seen this guy. */ free (tmp_name); return; } bh = NULL; val = hash_table->thumb_glue_size + 1; _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh); /* If we mark it 'Thumb', the disassembler will do a better job. */ myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC); myh->forced_local = 1; free (tmp_name); #define CHANGE_TO_ARM "__%s_change_to_arm" #define BACK_FROM_ARM "__%s_back_from_arm" /* Allocate another symbol to mark where we switch to Arm mode. */ tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (CHANGE_TO_ARM) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, CHANGE_TO_ARM, name); bh = NULL; val = hash_table->thumb_glue_size + 4, _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_LOCAL, s, val, NULL, TRUE, FALSE, &bh); free (tmp_name); s->size += THUMB2ARM_GLUE_SIZE; hash_table->thumb_glue_size += THUMB2ARM_GLUE_SIZE; return; } /* Add an entry to the code/data map for section SEC. */ static void elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma) { struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec); unsigned int newidx; if (sec_data->map == NULL) { sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map)); sec_data->mapcount = 0; sec_data->mapsize = 1; } newidx = sec_data->mapcount++; if (sec_data->mapcount > sec_data->mapsize) { sec_data->mapsize *= 2; sec_data->map = bfd_realloc (sec_data->map, sec_data->mapsize * sizeof (elf32_arm_section_map)); } sec_data->map[newidx].vma = vma; sec_data->map[newidx].type = type; } /* Record information about a VFP11 denorm-erratum veneer. Only ARM-mode veneers are handled for now. */ static bfd_vma record_vfp11_erratum_veneer (struct bfd_link_info *link_info, elf32_vfp11_erratum_list *branch, bfd *branch_bfd, asection *branch_sec, unsigned int offset) { asection *s; struct elf32_arm_link_hash_table *hash_table; char *tmp_name; struct elf_link_hash_entry *myh; struct bfd_link_hash_entry *bh; bfd_vma val; struct _arm_elf_section_data *sec_data; int errcount; elf32_vfp11_erratum_list *newerr; hash_table = elf32_arm_hash_table (link_info); BFD_ASSERT (hash_table != NULL); BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (hash_table->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME); sec_data = elf32_arm_section_data (s); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10); BFD_ASSERT (tmp_name); sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME, hash_table->num_vfp11_fixes); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE); BFD_ASSERT (myh == NULL); bh = NULL; val = hash_table->vfp11_erratum_glue_size; _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_FUNCTION | BSF_LOCAL, s, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; /* Link veneer back to calling location. */ errcount = ++(sec_data->erratumcount); newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list)); newerr->type = VFP11_ERRATUM_ARM_VENEER; newerr->vma = -1; newerr->u.v.branch = branch; newerr->u.v.id = hash_table->num_vfp11_fixes; branch->u.b.veneer = newerr; newerr->next = sec_data->erratumlist; sec_data->erratumlist = newerr; /* A symbol for the return from the veneer. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r", hash_table->num_vfp11_fixes); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE); if (myh != NULL) abort (); bh = NULL; val = offset + 4; _bfd_generic_link_add_one_symbol (link_info, branch_bfd, tmp_name, BSF_LOCAL, branch_sec, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; free (tmp_name); /* Generate a mapping symbol for the veneer section, and explicitly add an entry for that symbol to the code/data map for the section. */ if (hash_table->vfp11_erratum_glue_size == 0) { bh = NULL; /* FIXME: Creates an ARM symbol. Thumb mode will need attention if it ever requires this erratum fix. */ _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, "$a", BSF_LOCAL, s, 0, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE); myh->forced_local = 1; /* The elf32_arm_init_maps function only cares about symbols from input BFDs. We must make a note of this generated mapping symbol ourselves so that code byteswapping works properly in elf32_arm_write_section. */ elf32_arm_section_map_add (s, 'a', 0); } s->size += VFP11_ERRATUM_VENEER_SIZE; hash_table->vfp11_erratum_glue_size += VFP11_ERRATUM_VENEER_SIZE; hash_table->num_vfp11_fixes++; /* The offset of the veneer. */ return val; } /* Add the glue sections to ABFD. This function is called from the linker scripts in ld/emultempl/{armelf}.em. */ bfd_boolean bfd_elf32_arm_add_glue_sections_to_bfd (bfd *abfd, struct bfd_link_info *info) { flagword flags; asection *sec; /* If we are only performing a partial link do not bother adding the glue. */ if (info->relocatable) return TRUE; sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME); if (sec == NULL) { /* Note: we do not include the flag SEC_LINKER_CREATED, as this will prevent elf_link_input_bfd() from processing the contents of this section. */ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, ARM2THUMB_GLUE_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; /* Set the gc mark to prevent the section from being removed by garbage collection, despite the fact that no relocs refer to this section. */ sec->gc_mark = 1; } sec = bfd_get_section_by_name (abfd, THUMB2ARM_GLUE_SECTION_NAME); if (sec == NULL) { flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, THUMB2ARM_GLUE_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; sec->gc_mark = 1; } sec = bfd_get_section_by_name (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME); if (sec == NULL) { flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; sec->gc_mark = 1; } return TRUE; } /* Select a BFD to be used to hold the sections used by the glue code. This function is called from the linker scripts in ld/emultempl/ {armelf/pe}.em */ bfd_boolean bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *globals; /* If we are only performing a partial link do not bother getting a bfd to hold the glue. */ if (info->relocatable) return TRUE; /* Make sure we don't attach the glue sections to a dynamic object. */ BFD_ASSERT (!(abfd->flags & DYNAMIC)); globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); if (globals->bfd_of_glue_owner != NULL) return TRUE; /* Save the bfd for later use. */ globals->bfd_of_glue_owner = abfd; return TRUE; } static void check_use_blx(struct elf32_arm_link_hash_table *globals) { if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch) > 2) globals->use_blx = 1; } bfd_boolean bfd_elf32_arm_process_before_allocation (bfd *abfd, struct bfd_link_info *link_info) { Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *internal_relocs = NULL; Elf_Internal_Rela *irel, *irelend; bfd_byte *contents = NULL; asection *sec; struct elf32_arm_link_hash_table *globals; /* If we are only performing a partial link do not bother to construct any glue. */ if (link_info->relocatable) return TRUE; /* Here we have a bfd that is to be included on the link. We have a hook to do reloc rummaging, before section sizes are nailed down. */ globals = elf32_arm_hash_table (link_info); check_use_blx (globals); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); if (globals->byteswap_code && !bfd_big_endian (abfd)) { _bfd_error_handler (_("%B: BE8 images only valid in big-endian mode."), abfd); return FALSE; } /* Rummage around all the relocs and map the glue vectors. */ sec = abfd->sections; if (sec == NULL) return TRUE; for (; sec != NULL; sec = sec->next) { if (sec->reloc_count == 0) continue; if ((sec->flags & SEC_EXCLUDE) != 0) continue; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; /* Load the relocs. */ internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, (void *) NULL, (Elf_Internal_Rela *) NULL, FALSE); if (internal_relocs == NULL) goto error_return; irelend = internal_relocs + sec->reloc_count; for (irel = internal_relocs; irel < irelend; irel++) { long r_type; unsigned long r_index; struct elf_link_hash_entry *h; r_type = ELF32_R_TYPE (irel->r_info); r_index = ELF32_R_SYM (irel->r_info); /* These are the only relocation types we care about. */ if ( r_type != R_ARM_PC24 && r_type != R_ARM_PLT32 && r_type != R_ARM_CALL && r_type != R_ARM_JUMP24 && r_type != R_ARM_THM_CALL) continue; /* Get the section contents if we haven't done so already. */ if (contents == NULL) { /* Get cached copy if it exists. */ if (elf_section_data (sec)->this_hdr.contents != NULL) contents = elf_section_data (sec)->this_hdr.contents; else { /* Go get them off disk. */ if (! bfd_malloc_and_get_section (abfd, sec, &contents)) goto error_return; } } /* If the relocation is not against a symbol it cannot concern us. */ h = NULL; /* We don't care about local symbols. */ if (r_index < symtab_hdr->sh_info) continue; /* This is an external symbol. */ r_index -= symtab_hdr->sh_info; h = (struct elf_link_hash_entry *) elf_sym_hashes (abfd)[r_index]; /* If the relocation is against a static symbol it must be within the current section and so cannot be a cross ARM/Thumb relocation. */ if (h == NULL) continue; /* If the call will go through a PLT entry then we do not need glue. */ if (globals->splt != NULL && h->plt.offset != (bfd_vma) -1) continue; switch (r_type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: /* This one is a call from arm code. We need to look up the target of the call. If it is a thumb target, we insert glue. */ if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC && !(r_type == R_ARM_CALL && globals->use_blx)) record_arm_to_thumb_glue (link_info, h); break; case R_ARM_THM_CALL: /* This one is a call from thumb code. We look up the target of the call. If it is not a thumb target, we insert glue. */ if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC && !globals->use_blx && h->root.type != bfd_link_hash_undefweak) record_thumb_to_arm_glue (link_info, h); break; default: abort (); } } if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); contents = NULL; if (internal_relocs != NULL && elf_section_data (sec)->relocs != internal_relocs) free (internal_relocs); internal_relocs = NULL; } return TRUE; error_return: if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); if (internal_relocs != NULL && elf_section_data (sec)->relocs != internal_relocs) free (internal_relocs); return FALSE; } #endif /* Initialise maps of ARM/Thumb/data for input BFDs. */ void bfd_elf32_arm_init_maps (bfd *abfd) { Elf_Internal_Sym *isymbuf; Elf_Internal_Shdr *hdr; unsigned int i, localsyms; if (bfd_get_flavour (abfd) != bfd_target_elf_flavour || elf_tdata (abfd) == NULL) return; if ((abfd->flags & DYNAMIC) != 0) return; hdr = &elf_tdata (abfd)->symtab_hdr; localsyms = hdr->sh_info; /* Obtain a buffer full of symbols for this BFD. The hdr->sh_info field should contain the number of local symbols, which should come before any global symbols. Mapping symbols are always local. */ isymbuf = bfd_elf_get_elf_syms (abfd, hdr, localsyms, 0, NULL, NULL, NULL); /* No internal symbols read? Skip this BFD. */ if (isymbuf == NULL) return; for (i = 0; i < localsyms; i++) { Elf_Internal_Sym *isym = &isymbuf[i]; asection *sec = bfd_section_from_elf_index (abfd, isym->st_shndx); const char *name; if (sec != NULL && ELF_ST_BIND (isym->st_info) == STB_LOCAL) { name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, isym->st_name); if (bfd_is_arm_special_symbol_name (name, BFD_ARM_SPECIAL_SYM_TYPE_MAP)) elf32_arm_section_map_add (sec, name[1], isym->st_value); } } } void bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info) { struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd); /* We assume that ARMv7+ does not need the VFP11 denorm erratum fix. */ if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7) { switch (globals->vfp11_fix) { case BFD_ARM_VFP11_FIX_DEFAULT: case BFD_ARM_VFP11_FIX_NONE: globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; break; default: /* Give a warning, but do as the user requests anyway. */ (*_bfd_error_handler) (_("%B: warning: selected VFP11 erratum " "workaround is not necessary for target architecture"), obfd); } } else if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_DEFAULT) /* For earlier architectures, we might need the workaround, but do not enable it by default. If users is running with broken hardware, they must enable the erratum fix explicitly. */ globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; } enum bfd_arm_vfp11_pipe { VFP11_FMAC, VFP11_LS, VFP11_DS, VFP11_BAD }; /* Return a VFP register number. This is encoded as RX:X for single-precision registers, or X:RX for double-precision registers, where RX is the group of four bits in the instruction encoding and X is the single extension bit. RX and X fields are specified using their lowest (starting) bit. The return value is: 0...31: single-precision registers s0...s31 32...63: double-precision registers d0...d31. Although X should be zero for VFP11 (encoding d0...d15 only), we might encounter VFP3 instructions, so we allow the full range for DP registers. */ static unsigned int bfd_arm_vfp11_regno (unsigned int insn, bfd_boolean is_double, unsigned int rx, unsigned int x) { if (is_double) return (((insn >> rx) & 0xf) | (((insn >> x) & 1) << 4)) + 32; else return (((insn >> rx) & 0xf) << 1) | ((insn >> x) & 1); } /* Set bits in *WMASK according to a register number REG as encoded by bfd_arm_vfp11_regno(). Ignore d16-d31. */ static void bfd_arm_vfp11_write_mask (unsigned int *wmask, unsigned int reg) { if (reg < 32) *wmask |= 1 << reg; else if (reg < 48) *wmask |= 3 << ((reg - 32) * 2); } /* Return TRUE if WMASK overwrites anything in REGS. */ static bfd_boolean bfd_arm_vfp11_antidependency (unsigned int wmask, int *regs, int numregs) { int i; for (i = 0; i < numregs; i++) { unsigned int reg = regs[i]; if (reg < 32 && (wmask & (1 << reg)) != 0) return TRUE; reg -= 32; if (reg >= 16) continue; if ((wmask & (3 << (reg * 2))) != 0) return TRUE; } return FALSE; } /* In this function, we're interested in two things: finding input registers for VFP data-processing instructions, and finding the set of registers which arbitrary VFP instructions may write to. We use a 32-bit unsigned int to hold the written set, so FLDM etc. are easy to deal with (we're only interested in 32 SP registers or 16 dp registers, due to the VFP version implemented by the chip in question). DP registers are marked by setting both SP registers in the write mask). */ static enum bfd_arm_vfp11_pipe bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs, int *numregs) { enum bfd_arm_vfp11_pipe pipe = VFP11_BAD; bfd_boolean is_double = ((insn & 0xf00) == 0xb00) ? 1 : 0; if ((insn & 0x0f000e10) == 0x0e000a00) /* A data-processing insn. */ { unsigned int pqrs; unsigned int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22); unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5); pqrs = ((insn & 0x00800000) >> 20) | ((insn & 0x00300000) >> 19) | ((insn & 0x00000040) >> 6); switch (pqrs) { case 0: /* fmac[sd]. */ case 1: /* fnmac[sd]. */ case 2: /* fmsc[sd]. */ case 3: /* fnmsc[sd]. */ pipe = VFP11_FMAC; bfd_arm_vfp11_write_mask (destmask, fd); regs[0] = fd; regs[1] = bfd_arm_vfp11_regno (insn, is_double, 16, 7); /* Fn. */ regs[2] = fm; *numregs = 3; break; case 4: /* fmul[sd]. */ case 5: /* fnmul[sd]. */ case 6: /* fadd[sd]. */ case 7: /* fsub[sd]. */ pipe = VFP11_FMAC; goto vfp_binop; case 8: /* fdiv[sd]. */ pipe = VFP11_DS; vfp_binop: bfd_arm_vfp11_write_mask (destmask, fd); regs[0] = bfd_arm_vfp11_regno (insn, is_double, 16, 7); /* Fn. */ regs[1] = fm; *numregs = 2; break; case 15: /* extended opcode. */ { unsigned int extn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1); switch (extn) { case 0: /* fcpy[sd]. */ case 1: /* fabs[sd]. */ case 2: /* fneg[sd]. */ case 8: /* fcmp[sd]. */ case 9: /* fcmpe[sd]. */ case 10: /* fcmpz[sd]. */ case 11: /* fcmpez[sd]. */ case 16: /* fuito[sd]. */ case 17: /* fsito[sd]. */ case 24: /* ftoui[sd]. */ case 25: /* ftouiz[sd]. */ case 26: /* ftosi[sd]. */ case 27: /* ftosiz[sd]. */ /* These instructions will not bounce due to underflow. */ *numregs = 0; pipe = VFP11_FMAC; break; case 3: /* fsqrt[sd]. */ /* fsqrt cannot underflow, but it can (perhaps) overwrite registers to cause the erratum in previous instructions. */ bfd_arm_vfp11_write_mask (destmask, fd); pipe = VFP11_DS; break; case 15: /* fcvt{ds,sd}. */ { int rnum = 0; bfd_arm_vfp11_write_mask (destmask, fd); /* Only FCVTSD can underflow. */ if ((insn & 0x100) != 0) regs[rnum++] = fm; *numregs = rnum; pipe = VFP11_FMAC; } break; default: return VFP11_BAD; } } break; default: return VFP11_BAD; } } /* Two-register transfer. */ else if ((insn & 0x0fe00ed0) == 0x0c400a10) { unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5); if ((insn & 0x100000) == 0) { if (is_double) bfd_arm_vfp11_write_mask (destmask, fm); else { bfd_arm_vfp11_write_mask (destmask, fm); bfd_arm_vfp11_write_mask (destmask, fm + 1); } } pipe = VFP11_LS; } else if ((insn & 0x0e100e00) == 0x0c100a00) /* A load insn. */ { int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22); unsigned int puw = ((insn >> 21) & 0x1) | (((insn >> 23) & 3) << 1); switch (puw) { case 0: /* Two-reg transfer. We should catch these above. */ abort (); case 2: /* fldm[sdx]. */ case 3: case 5: { unsigned int i, offset = insn & 0xff; if (is_double) offset >>= 1; for (i = fd; i < fd + offset; i++) bfd_arm_vfp11_write_mask (destmask, i); } break; case 4: /* fld[sd]. */ case 6: bfd_arm_vfp11_write_mask (destmask, fd); break; default: return VFP11_BAD; } pipe = VFP11_LS; } /* Single-register transfer. Note L==0. */ else if ((insn & 0x0f100e10) == 0x0e000a10) { unsigned int opcode = (insn >> 21) & 7; unsigned int fn = bfd_arm_vfp11_regno (insn, is_double, 16, 7); switch (opcode) { case 0: /* fmsr/fmdlr. */ case 1: /* fmdhr. */ /* Mark fmdhr and fmdlr as writing to the whole of the DP destination register. I don't know if this is exactly right, but it is the conservative choice. */ bfd_arm_vfp11_write_mask (destmask, fn); break; case 7: /* fmxr. */ break; } pipe = VFP11_LS; } return pipe; } static int elf32_arm_compare_mapping (const void * a, const void * b); /* Look for potentially-troublesome code sequences which might trigger the VFP11 denormal/antidependency erratum. See, e.g., the ARM1136 errata sheet (available from ARM) for details of the erratum. A short version is described in ld.texinfo. */ bfd_boolean bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info) { asection *sec; bfd_byte *contents = NULL; int state = 0; int regs[3], numregs = 0; struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR); /* We use a simple FSM to match troublesome VFP11 instruction sequences. The states transition as follows: 0 -> 1 (vector) or 0 -> 2 (scalar) A VFP FMAC-pipeline instruction has been seen. Fill regs[0]..regs[numregs-1] with its input operands. Remember this instruction in 'first_fmac'. 1 -> 2 Any instruction, except for a VFP instruction which overwrites regs[*]. 1 -> 3 [ -> 0 ] or 2 -> 3 [ -> 0 ] A VFP instruction has been seen which overwrites any of regs[*]. We must make a veneer! Reset state to 0 before examining next instruction. 2 -> 0 If we fail to match anything in state 2, reset to state 0 and reset the instruction pointer to the instruction after 'first_fmac'. If the VFP11 vector mode is in use, there must be at least two unrelated instructions between anti-dependent VFP11 instructions to properly avoid triggering the erratum, hence the use of the extra state 1. */ /* If we are only performing a partial link do not bother to construct any glue. */ if (link_info->relocatable) return TRUE; /* We should have chosen a fix type by the time we get here. */ BFD_ASSERT (globals->vfp11_fix != BFD_ARM_VFP11_FIX_DEFAULT); if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_NONE) return TRUE; /* Skip if this bfd does not correspond to an ELF image. */ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) return TRUE; for (sec = abfd->sections; sec != NULL; sec = sec->next) { unsigned int i, span, first_fmac = 0, veneer_of_insn = 0; struct _arm_elf_section_data *sec_data; /* If we don't have executable progbits, we're not interested in this section. Also skip if section is to be excluded. */ if (elf_section_type (sec) != SHT_PROGBITS || (elf_section_flags (sec) & SHF_EXECINSTR) == 0 || (sec->flags & SEC_EXCLUDE) != 0 || strcmp (sec->name, VFP11_ERRATUM_VENEER_SECTION_NAME) == 0) continue; sec_data = elf32_arm_section_data (sec); if (sec_data->mapcount == 0) continue; if (elf_section_data (sec)->this_hdr.contents != NULL) contents = elf_section_data (sec)->this_hdr.contents; else if (! bfd_malloc_and_get_section (abfd, sec, &contents)) goto error_return; qsort (sec_data->map, sec_data->mapcount, sizeof (elf32_arm_section_map), elf32_arm_compare_mapping); for (span = 0; span < sec_data->mapcount; span++) { unsigned int span_start = sec_data->map[span].vma; unsigned int span_end = (span == sec_data->mapcount - 1) ? sec->size : sec_data->map[span + 1].vma; char span_type = sec_data->map[span].type; /* FIXME: Only ARM mode is supported at present. We may need to support Thumb-2 mode also at some point. */ if (span_type != 'a') continue; for (i = span_start; i < span_end;) { unsigned int next_i = i + 4; unsigned int insn = bfd_big_endian (abfd) ? (contents[i] << 24) | (contents[i + 1] << 16) | (contents[i + 2] << 8) | contents[i + 3] : (contents[i + 3] << 24) | (contents[i + 2] << 16) | (contents[i + 1] << 8) | contents[i]; unsigned int writemask = 0; enum bfd_arm_vfp11_pipe pipe; switch (state) { case 0: pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, regs, &numregs); /* I'm assuming the VFP11 erratum can trigger with denorm operands on either the FMAC or the DS pipeline. This might lead to slightly overenthusiastic veneer insertion. */ if (pipe == VFP11_FMAC || pipe == VFP11_DS) { state = use_vector ? 1 : 2; first_fmac = i; veneer_of_insn = insn; } break; case 1: { int other_regs[3], other_numregs; pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, other_regs, &other_numregs); if (pipe != VFP11_BAD && bfd_arm_vfp11_antidependency (writemask, regs, numregs)) state = 3; else state = 2; } break; case 2: { int other_regs[3], other_numregs; pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, other_regs, &other_numregs); if (pipe != VFP11_BAD && bfd_arm_vfp11_antidependency (writemask, regs, numregs)) state = 3; else { state = 0; next_i = first_fmac + 4; } } break; case 3: abort (); /* Should be unreachable. */ } if (state == 3) { elf32_vfp11_erratum_list *newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list)); int errcount; errcount = ++(elf32_arm_section_data (sec)->erratumcount); newerr->u.b.vfp_insn = veneer_of_insn; switch (span_type) { case 'a': newerr->type = VFP11_ERRATUM_BRANCH_TO_ARM_VENEER; break; default: abort (); } record_vfp11_erratum_veneer (link_info, newerr, abfd, sec, first_fmac); newerr->vma = -1; newerr->next = sec_data->erratumlist; sec_data->erratumlist = newerr; state = 0; } i = next_i; } } if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); contents = NULL; } return TRUE; error_return: if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); return FALSE; } /* Find virtual-memory addresses for VFP11 erratum veneers and return locations after sections have been laid out, using specially-named symbols. */ void bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd, struct bfd_link_info *link_info) { asection *sec; struct elf32_arm_link_hash_table *globals; char *tmp_name; if (link_info->relocatable) return; /* Skip if this bfd does not correspond to an ELF image. */ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) return; globals = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10); for (sec = abfd->sections; sec != NULL; sec = sec->next) { struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec); elf32_vfp11_erratum_list *errnode = sec_data->erratumlist; for (; errnode != NULL; errnode = errnode->next) { struct elf_link_hash_entry *myh; bfd_vma vma; switch (errnode->type) { case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER: case VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER: /* Find veneer symbol. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME, errnode->u.b.veneer->u.v.id); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer " "`%s'"), abfd, tmp_name); vma = myh->root.u.def.section->output_section->vma + myh->root.u.def.section->output_offset + myh->root.u.def.value; errnode->u.b.veneer->vma = vma; break; case VFP11_ERRATUM_ARM_VENEER: case VFP11_ERRATUM_THUMB_VENEER: /* Find return location. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r", errnode->u.v.id); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer " "`%s'"), abfd, tmp_name); vma = myh->root.u.def.section->output_section->vma + myh->root.u.def.section->output_offset + myh->root.u.def.value; errnode->u.v.branch->vma = vma; break; default: abort (); } } } free (tmp_name); } /* Set target relocation values needed during linking. */ void bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd, struct bfd_link_info *link_info, int target1_is_rel, char * target2_type, int fix_v4bx, int use_blx, bfd_arm_vfp11_fix vfp11_fix, int no_enum_warn, int pic_veneer) { struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (link_info); globals->target1_is_rel = target1_is_rel; if (strcmp (target2_type, "rel") == 0) globals->target2_reloc = R_ARM_REL32; else if (strcmp (target2_type, "abs") == 0) globals->target2_reloc = R_ARM_ABS32; else if (strcmp (target2_type, "got-rel") == 0) globals->target2_reloc = R_ARM_GOT_PREL; else { _bfd_error_handler (_("Invalid TARGET2 relocation type '%s'."), target2_type); } globals->fix_v4bx = fix_v4bx; globals->use_blx |= use_blx; globals->vfp11_fix = vfp11_fix; globals->pic_veneer = pic_veneer; elf32_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn; } /* The thumb form of a long branch is a bit finicky, because the offset encoding is split over two fields, each in it's own instruction. They can occur in any order. So given a thumb form of long branch, and an offset, insert the offset into the thumb branch and return finished instruction. It takes two thumb instructions to encode the target address. Each has 11 bits to invest. The upper 11 bits are stored in one (identified by H-0.. see below), the lower 11 bits are stored in the other (identified by H-1). Combine together and shifted left by 1 (it's a half word address) and there you have it. Op: 1111 = F, H-0, upper address-0 = 000 Op: 1111 = F, H-1, lower address-0 = 800 They can be ordered either way, but the arm tools I've seen always put the lower one first. It probably doesn't matter. krk@cygnus.com XXX: Actually the order does matter. The second instruction (H-1) moves the computed address into the PC, so it must be the second one in the sequence. The problem, however is that whilst little endian code stores the instructions in HI then LOW order, big endian code does the reverse. nickc@cygnus.com. */ #define LOW_HI_ORDER 0xF800F000 #define HI_LOW_ORDER 0xF000F800 static insn32 insert_thumb_branch (insn32 br_insn, int rel_off) { unsigned int low_bits; unsigned int high_bits; BFD_ASSERT ((rel_off & 1) != 1); rel_off >>= 1; /* Half word aligned address. */ low_bits = rel_off & 0x000007FF; /* The bottom 11 bits. */ high_bits = (rel_off >> 11) & 0x000007FF; /* The top 11 bits. */ if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER) br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits; else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER) br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits; else /* FIXME: abort is probably not the right call. krk@cygnus.com */ abort (); /* Error - not a valid branch instruction form. */ return br_insn; } /* Store an Arm insn into an output section not processed by elf32_arm_write_section. */ static void put_arm_insn (struct elf32_arm_link_hash_table *htab, bfd * output_bfd, bfd_vma val, void * ptr) { if (htab->byteswap_code != bfd_little_endian (output_bfd)) bfd_putl32 (val, ptr); else bfd_putb32 (val, ptr); } /* Store a 16-bit Thumb insn into an output section not processed by elf32_arm_write_section. */ static void put_thumb_insn (struct elf32_arm_link_hash_table *htab, bfd * output_bfd, bfd_vma val, void * ptr) { if (htab->byteswap_code != bfd_little_endian (output_bfd)) bfd_putl16 (val, ptr); else bfd_putb16 (val, ptr); } /* Thumb code calling an ARM function. */ static int elf32_thumb_to_arm_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * hit_data, asection * sym_sec, bfd_vma offset, bfd_signed_vma addend, bfd_vma val, char **error_message) { asection * s = 0; bfd_vma my_offset; unsigned long int tmp; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; myh = find_thumb_glue (info, name, error_message); if (myh == NULL) return FALSE; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); my_offset = myh->root.u.def.value; s = bfd_get_section_by_name (globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); if ((my_offset & 0x01) == 0x01) { if (sym_sec != NULL && sym_sec->owner != NULL && !INTERWORK_FLAG (sym_sec->owner)) { (*_bfd_error_handler) (_("%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: thumb call to arm"), sym_sec->owner, input_bfd, name); return FALSE; } --my_offset; myh->root.u.def.value = my_offset; put_thumb_insn (globals, output_bfd, (bfd_vma) t2a1_bx_pc_insn, s->contents + my_offset); put_thumb_insn (globals, output_bfd, (bfd_vma) t2a2_noop_insn, s->contents + my_offset + 2); ret_offset = /* Address of destination of the stub. */ ((bfd_signed_vma) val) - ((bfd_signed_vma) /* Offset from the start of the current section to the start of the stubs. */ (s->output_offset /* Offset of the start of this stub from the start of the stubs. */ + my_offset /* Address of the start of the current section. */ + s->output_section->vma) /* The branch instruction is 4 bytes into the stub. */ + 4 /* ARM branches work from the pc of the instruction + 8. */ + 8); put_arm_insn (globals, output_bfd, (bfd_vma) t2a3_b_insn | ((ret_offset >> 2) & 0x00FFFFFF), s->contents + my_offset + 4); } BFD_ASSERT (my_offset <= globals->thumb_glue_size); /* Now go back and fix up the original BL insn to point to here. */ ret_offset = /* Address of where the stub is located. */ (s->output_section->vma + s->output_offset + my_offset) /* Address of where the BL is located. */ - (input_section->output_section->vma + input_section->output_offset + offset) /* Addend in the relocation. */ - addend /* Biassing for PC-relative addressing. */ - 8; tmp = bfd_get_32 (input_bfd, hit_data - input_section->vma); bfd_put_32 (output_bfd, (bfd_vma) insert_thumb_branch (tmp, ret_offset), hit_data - input_section->vma); return TRUE; } /* Populate an Arm to Thumb stub. Returns the stub symbol. */ static struct elf_link_hash_entry * elf32_arm_create_thumb_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * sym_sec, bfd_vma val, asection *s, char **error_message) { bfd_vma my_offset; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; myh = find_arm_glue (info, name, error_message); if (myh == NULL) return NULL; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); my_offset = myh->root.u.def.value; if ((my_offset & 0x01) == 0x01) { if (sym_sec != NULL && sym_sec->owner != NULL && !INTERWORK_FLAG (sym_sec->owner)) { (*_bfd_error_handler) (_("%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: arm call to thumb"), sym_sec->owner, input_bfd, name); } --my_offset; myh->root.u.def.value = my_offset; if (info->shared || globals->root.is_relocatable_executable || globals->pic_veneer) { /* For relocatable objects we can't use absolute addresses, so construct the address from a relative offset. */ /* TODO: If the offset is small it's probably worth constructing the address with adds. */ put_arm_insn (globals, output_bfd, (bfd_vma) a2t1p_ldr_insn, s->contents + my_offset); put_arm_insn (globals, output_bfd, (bfd_vma) a2t2p_add_pc_insn, s->contents + my_offset + 4); put_arm_insn (globals, output_bfd, (bfd_vma) a2t3p_bx_r12_insn, s->contents + my_offset + 8); /* Adjust the offset by 4 for the position of the add, and 8 for the pipeline offset. */ ret_offset = (val - (s->output_offset + s->output_section->vma + my_offset + 12)) | 1; bfd_put_32 (output_bfd, ret_offset, s->contents + my_offset + 12); } else if (globals->use_blx) { put_arm_insn (globals, output_bfd, (bfd_vma) a2t1v5_ldr_insn, s->contents + my_offset); /* It's a thumb address. Add the low order bit. */ bfd_put_32 (output_bfd, val | a2t2v5_func_addr_insn, s->contents + my_offset + 4); } else { put_arm_insn (globals, output_bfd, (bfd_vma) a2t1_ldr_insn, s->contents + my_offset); put_arm_insn (globals, output_bfd, (bfd_vma) a2t2_bx_r12_insn, s->contents + my_offset + 4); /* It's a thumb address. Add the low order bit. */ bfd_put_32 (output_bfd, val | a2t3_func_addr_insn, s->contents + my_offset + 8); } } BFD_ASSERT (my_offset <= globals->arm_glue_size); return myh; } /* Arm code calling a Thumb function. */ static int elf32_arm_to_thumb_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * hit_data, asection * sym_sec, bfd_vma offset, bfd_signed_vma addend, bfd_vma val, char **error_message) { unsigned long int tmp; bfd_vma my_offset; asection * s; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); myh = elf32_arm_create_thumb_stub (info, name, input_bfd, output_bfd, sym_sec, val, s, error_message); if (!myh) return FALSE; my_offset = myh->root.u.def.value; tmp = bfd_get_32 (input_bfd, hit_data); tmp = tmp & 0xFF000000; /* Somehow these are both 4 too far, so subtract 8. */ ret_offset = (s->output_offset + my_offset + s->output_section->vma - (input_section->output_offset + input_section->output_section->vma + offset + addend) - 8); tmp = tmp | ((ret_offset >> 2) & 0x00FFFFFF); bfd_put_32 (output_bfd, (bfd_vma) tmp, hit_data - input_section->vma); return TRUE; } /* Populate Arm stub for an exported Thumb function. */ static bfd_boolean elf32_arm_to_thumb_export_stub (struct elf_link_hash_entry *h, void * inf) { struct bfd_link_info * info = (struct bfd_link_info *) inf; asection * s; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_entry *eh; struct elf32_arm_link_hash_table * globals; asection *sec; bfd_vma val; char *error_message; eh = elf32_arm_hash_entry(h); /* Allocate stubs for exported Thumb functions on v4t. */ if (eh->export_glue == NULL) return TRUE; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); sec = eh->export_glue->root.u.def.section; BFD_ASSERT (sec->output_section != NULL); val = eh->export_glue->root.u.def.value + sec->output_offset + sec->output_section->vma; myh = elf32_arm_create_thumb_stub (info, h->root.root.string, h->root.u.def.section->owner, globals->obfd, sec, val, s, &error_message); BFD_ASSERT (myh); return TRUE; } /* Generate Arm stubs for exported Thumb symbols. */ static void elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *link_info) { struct elf32_arm_link_hash_table * globals; if (!link_info) return; globals = elf32_arm_hash_table (link_info); /* If blx is available then exported Thumb symbols are OK and there is nothing to do. */ if (globals->use_blx) return; elf_link_hash_traverse (&globals->root, elf32_arm_to_thumb_export_stub, link_info); } /* Some relocations map to different relocations depending on the target. Return the real relocation. */ static int arm_real_reloc_type (struct elf32_arm_link_hash_table * globals, int r_type) { switch (r_type) { case R_ARM_TARGET1: if (globals->target1_is_rel) return R_ARM_REL32; else return R_ARM_ABS32; case R_ARM_TARGET2: return globals->target2_reloc; default: return r_type; } } /* Return the base VMA address which should be subtracted from real addresses when resolving @dtpoff relocation. This is PT_TLS segment p_vaddr. */ static bfd_vma dtpoff_base (struct bfd_link_info *info) { /* If tls_sec is NULL, we should have signalled an error already. */ if (elf_hash_table (info)->tls_sec == NULL) return 0; return elf_hash_table (info)->tls_sec->vma; } /* Return the relocation value for @tpoff relocation if STT_TLS virtual address is ADDRESS. */ static bfd_vma tpoff (struct bfd_link_info *info, bfd_vma address) { struct elf_link_hash_table *htab = elf_hash_table (info); bfd_vma base; /* If tls_sec is NULL, we should have signalled an error already. */ if (htab->tls_sec == NULL) return 0; base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power); return address - htab->tls_sec->vma + base; } /* Perform an R_ARM_ABS12 relocation on the field pointed to by DATA. VALUE is the relocation value. */ static bfd_reloc_status_type elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value) { if (value > 0xfff) return bfd_reloc_overflow; value |= bfd_get_32 (abfd, data) & 0xfffff000; bfd_put_32 (abfd, value, data); return bfd_reloc_ok; } /* For a given value of n, calculate the value of G_n as required to deal with group relocations. We return it in the form of an encoded constant-and-rotation, together with the final residual. If n is specified as less than zero, then final_residual is filled with the input value and no further action is performed. */ static bfd_vma calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual) { int current_n; bfd_vma g_n; bfd_vma encoded_g_n = 0; bfd_vma residual = value; /* Also known as Y_n. */ for (current_n = 0; current_n <= n; current_n++) { int shift; /* Calculate which part of the value to mask. */ if (residual == 0) shift = 0; else { int msb; /* Determine the most significant bit in the residual and align the resulting value to a 2-bit boundary. */ for (msb = 30; msb >= 0; msb -= 2) if (residual & (3 << msb)) break; /* The desired shift is now (msb - 6), or zero, whichever is the greater. */ shift = msb - 6; if (shift < 0) shift = 0; } /* Calculate g_n in 32-bit as well as encoded constant+rotation form. */ g_n = residual & (0xff << shift); encoded_g_n = (g_n >> shift) | ((g_n <= 0xff ? 0 : (32 - shift) / 2) << 8); /* Calculate the residual for the next time around. */ residual &= ~g_n; } *final_residual = residual; return encoded_g_n; } /* Given an ARM instruction, determine whether it is an ADD or a SUB. Returns 1 if it is an ADD, -1 if it is a SUB, and 0 otherwise. */ static int identify_add_or_sub(bfd_vma insn) { int opcode = insn & 0x1e00000; if (opcode == 1 << 23) /* ADD */ return 1; if (opcode == 1 << 22) /* SUB */ return -1; return 0; } /* Determine if we're dealing with a Thumb-2 object. */ static int using_thumb2 (struct elf32_arm_link_hash_table *globals) { int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch); return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7; } /* Perform a relocation as part of a final link. */ static bfd_reloc_status_type elf32_arm_final_link_relocate (reloc_howto_type * howto, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * contents, Elf_Internal_Rela * rel, bfd_vma value, struct bfd_link_info * info, asection * sym_sec, const char * sym_name, int sym_flags, struct elf_link_hash_entry * h, bfd_boolean * unresolved_reloc_p, char **error_message) { unsigned long r_type = howto->type; unsigned long r_symndx; bfd_byte * hit_data = contents + rel->r_offset; bfd * dynobj = NULL; Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; bfd_vma * local_got_offsets; asection * sgot = NULL; asection * splt = NULL; asection * sreloc = NULL; bfd_vma addend; bfd_signed_vma signed_addend; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); /* Some relocation type map to different relocations depending on the target. We pick the right one here. */ r_type = arm_real_reloc_type (globals, r_type); if (r_type != howto->type) howto = elf32_arm_howto_from_type (r_type); /* If the start address has been set, then set the EF_ARM_HASENTRY flag. Setting this more than once is redundant, but the cost is not too high, and it keeps the code simple. The test is done here, rather than somewhere else, because the start address is only set just before the final link commences. Note - if the user deliberately sets a start address of 0, the flag will not be set. */ if (bfd_get_start_address (output_bfd) != 0) elf_elfheader (output_bfd)->e_flags |= EF_ARM_HASENTRY; dynobj = elf_hash_table (info)->dynobj; if (dynobj) { sgot = bfd_get_section_by_name (dynobj, ".got"); splt = bfd_get_section_by_name (dynobj, ".plt"); } symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); local_got_offsets = elf_local_got_offsets (input_bfd); r_symndx = ELF32_R_SYM (rel->r_info); if (globals->use_rel) { addend = bfd_get_32 (input_bfd, hit_data) & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { signed_addend = -1; signed_addend &= ~ howto->src_mask; signed_addend |= addend; } else signed_addend = addend; } else addend = signed_addend = rel->r_addend; switch (r_type) { case R_ARM_NONE: /* We don't need to find a value for this symbol. It's just a marker. */ *unresolved_reloc_p = FALSE; return bfd_reloc_ok; case R_ARM_ABS12: if (!globals->vxworks_p) return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend); case R_ARM_PC24: case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_XPC25: case R_ARM_PREL31: case R_ARM_PLT32: /* Handle relocations which should use the PLT entry. ABS32/REL32 will use the symbol's value, which may point to a PLT entry, but we don't need to handle that here. If we created a PLT entry, all branches in this object should go to it. */ if ((r_type != R_ARM_ABS32 && r_type != R_ARM_REL32 && r_type != R_ARM_ABS32_NOI && r_type != R_ARM_REL32_NOI) && h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) { /* If we've created a .plt section, and assigned a PLT entry to this function, it should not be known to bind locally. If it were, we would have cleared the PLT entry. */ BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); value = (splt->output_section->vma + splt->output_offset + h->plt.offset); *unresolved_reloc_p = FALSE; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } /* When generating a shared object or relocatable executable, these relocations are copied into the output file to be resolved at run time. */ if ((info->shared || globals->root.is_relocatable_executable) && (input_section->flags & SEC_ALLOC) && ((r_type != R_ARM_REL32 && r_type != R_ARM_REL32_NOI) || !SYMBOL_CALLS_LOCAL (info, h)) && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak) && r_type != R_ARM_PC24 && r_type != R_ARM_CALL && r_type != R_ARM_JUMP24 && r_type != R_ARM_PREL31 && r_type != R_ARM_PLT32) { Elf_Internal_Rela outrel; bfd_byte *loc; bfd_boolean skip, relocate; *unresolved_reloc_p = FALSE; if (sreloc == NULL) { const char * name; name = (bfd_elf_string_from_elf_section (input_bfd, elf_elfheader (input_bfd)->e_shstrndx, elf_section_data (input_section)->rel_hdr.sh_name)); if (name == NULL) return bfd_reloc_notsupported; BFD_ASSERT (reloc_section_p (globals, name, input_section)); sreloc = bfd_get_section_by_name (dynobj, name); BFD_ASSERT (sreloc != NULL); } skip = FALSE; relocate = FALSE; outrel.r_addend = addend; outrel.r_offset = _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset); if (outrel.r_offset == (bfd_vma) -1) skip = TRUE; else if (outrel.r_offset == (bfd_vma) -2) skip = TRUE, relocate = TRUE; outrel.r_offset += (input_section->output_section->vma + input_section->output_offset); if (skip) memset (&outrel, 0, sizeof outrel); else if (h != NULL && h->dynindx != -1 && (!info->shared || !info->symbolic || !h->def_regular)) outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); else { int symbol; /* This symbol is local, or marked to become local. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; if (globals->symbian_p) { asection *osec; /* On Symbian OS, the data segment and text segement can be relocated independently. Therefore, we must indicate the segment to which this relocation is relative. The BPABI allows us to use any symbol in the right segment; we just use the section symbol as it is convenient. (We cannot use the symbol given by "h" directly as it will not appear in the dynamic symbol table.) Note that the dynamic linker ignores the section symbol value, so we don't subtract osec->vma from the emitted reloc addend. */ if (sym_sec) osec = sym_sec->output_section; else osec = input_section->output_section; symbol = elf_section_data (osec)->dynindx; if (symbol == 0) { struct elf_link_hash_table *htab = elf_hash_table (info); if ((osec->flags & SEC_READONLY) == 0 && htab->data_index_section != NULL) osec = htab->data_index_section; else osec = htab->text_index_section; symbol = elf_section_data (osec)->dynindx; } BFD_ASSERT (symbol != 0); } else /* On SVR4-ish systems, the dynamic loader cannot relocate the text and data segments independently, so the symbol does not matter. */ symbol = 0; outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE); if (globals->use_rel) relocate = TRUE; else outrel.r_addend += value; } loc = sreloc->contents; loc += sreloc->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); /* If this reloc is against an external symbol, we do not want to fiddle with the addend. Otherwise, we need to include the symbol value so that it becomes an addend for the dynamic reloc. */ if (! relocate) return bfd_reloc_ok; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, (bfd_vma) 0); } else switch (r_type) { case R_ARM_ABS12: return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend); case R_ARM_XPC25: /* Arm BLX instruction. */ case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: /* Arm B/BL instruction */ case R_ARM_PLT32: if (r_type == R_ARM_XPC25) { /* Check for Arm calling Arm function. */ /* FIXME: Should we translate the instruction into a BL instruction instead ? */ if (sym_flags != STT_ARM_TFUNC) (*_bfd_error_handler) (_("\%B: Warning: Arm BLX instruction targets Arm function '%s'."), input_bfd, h ? h->root.root.string : "(local)"); } else if (r_type != R_ARM_CALL || !globals->use_blx) { /* Check for Arm calling Thumb function. */ if (sym_flags == STT_ARM_TFUNC) { if (elf32_arm_to_thumb_stub (info, sym_name, input_bfd, output_bfd, input_section, hit_data, sym_sec, rel->r_offset, signed_addend, value, error_message)) return bfd_reloc_ok; else return bfd_reloc_dangerous; } } /* The ARM ELF ABI says that this reloc is computed as: S - P + A where: S is the address of the symbol in the relocation. P is address of the instruction being relocated. A is the addend (extracted from the instruction) in bytes. S is held in 'value'. P is the base address of the section containing the instruction plus the offset of the reloc into that section, ie: (input_section->output_section->vma + input_section->output_offset + rel->r_offset). A is the addend, converted into bytes, ie: (signed_addend * 4) Note: None of these operations have knowledge of the pipeline size of the processor, thus it is up to the assembler to encode this information into the addend. */ value -= (input_section->output_section->vma + input_section->output_offset); value -= rel->r_offset; if (globals->use_rel) value += (signed_addend << howto->size); else /* RELA addends do not have to be adjusted by howto->size. */ value += signed_addend; signed_addend = value; signed_addend >>= howto->rightshift; /* A branch to an undefined weak symbol is turned into a jump to the next instruction. */ if (h && h->root.type == bfd_link_hash_undefweak) { value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000) | 0x0affffff; } else { /* Perform a signed range check. */ if ( signed_addend > ((bfd_signed_vma) (howto->dst_mask >> 1)) || signed_addend < - ((bfd_signed_vma) ((howto->dst_mask + 1) >> 1))) return bfd_reloc_overflow; addend = (value & 2); value = (signed_addend & howto->dst_mask) | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask)); /* Set the H bit in the BLX instruction. */ if (sym_flags == STT_ARM_TFUNC) { if (addend) value |= (1 << 24); else value &= ~(bfd_vma)(1 << 24); } if (r_type == R_ARM_CALL) { /* Select the correct instruction (BL or BLX). */ if (sym_flags == STT_ARM_TFUNC) value |= (1 << 28); else { value &= ~(bfd_vma)(1 << 28); value |= (1 << 24); } } } break; case R_ARM_ABS32: value += addend; if (sym_flags == STT_ARM_TFUNC) value |= 1; break; case R_ARM_ABS32_NOI: value += addend; break; case R_ARM_REL32: value += addend; if (sym_flags == STT_ARM_TFUNC) value |= 1; value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); break; case R_ARM_REL32_NOI: value += addend; value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); break; case R_ARM_PREL31: value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); value += signed_addend; if (! h || h->root.type != bfd_link_hash_undefweak) { /* Check for overflow */ if ((value ^ (value >> 1)) & (1 << 30)) return bfd_reloc_overflow; } value &= 0x7fffffff; value |= (bfd_get_32 (input_bfd, hit_data) & 0x80000000); if (sym_flags == STT_ARM_TFUNC) value |= 1; break; } bfd_put_32 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_ABS8: value += addend; if ((long) value > 0x7f || (long) value < -0x80) return bfd_reloc_overflow; bfd_put_8 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_ABS16: value += addend; if ((long) value > 0x7fff || (long) value < -0x8000) return bfd_reloc_overflow; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_THM_ABS5: /* Support ldr and str instructions for the thumb. */ if (globals->use_rel) { /* Need to refetch addend. */ addend = bfd_get_16 (input_bfd, hit_data) & howto->src_mask; /* ??? Need to determine shift amount from operand size. */ addend >>= howto->rightshift; } value += addend; /* ??? Isn't value unsigned? */ if ((long) value > 0x1f || (long) value < -0x10) return bfd_reloc_overflow; /* ??? Value needs to be properly shifted into place first. */ value |= bfd_get_16 (input_bfd, hit_data) & 0xf83f; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_THM_ALU_PREL_11_0: /* Corresponds to: addw.w reg, pc, #offset (and similarly for subw). */ { bfd_vma insn; bfd_signed_vma relocation; insn = (bfd_get_16 (input_bfd, hit_data) << 16) | bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { signed_addend = (insn & 0xff) | ((insn & 0x7000) >> 4) | ((insn & (1 << 26)) >> 15); if (insn & 0xf00000) signed_addend = -signed_addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); value = llabs (relocation); if (value >= 0x1000) return bfd_reloc_overflow; insn = (insn & 0xfb0f8f00) | (value & 0xff) | ((value & 0x700) << 4) | ((value & 0x800) << 15); if (relocation < 0) insn |= 0xa00000; bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); return bfd_reloc_ok; } case R_ARM_THM_PC12: /* Corresponds to: ldr.w reg, [pc, #offset]. */ { bfd_vma insn; bfd_signed_vma relocation; insn = (bfd_get_16 (input_bfd, hit_data) << 16) | bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { signed_addend = insn & 0xfff; if (!(insn & (1 << 23))) signed_addend = -signed_addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); value = llabs (relocation); if (value >= 0x1000) return bfd_reloc_overflow; insn = (insn & 0xff7ff000) | value; if (relocation >= 0) insn |= (1 << 23); bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); return bfd_reloc_ok; } case R_ARM_THM_XPC22: case R_ARM_THM_CALL: /* Thumb BL (branch long instruction). */ { bfd_vma relocation; bfd_vma reloc_sign; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max; bfd_signed_vma reloc_signed_min; bfd_vma check; bfd_signed_vma signed_check; int bitsize; int thumb2 = using_thumb2 (globals); /* A branch to an undefined weak symbol is turned into a jump to the next instruction. */ if (h && h->root.type == bfd_link_hash_undefweak) { bfd_put_16 (input_bfd, 0xe000, hit_data); bfd_put_16 (input_bfd, 0xbf00, hit_data + 2); return bfd_reloc_ok; } /* Fetch the addend. We use the Thumb-2 encoding (backwards compatible with Thumb-1) involving the J1 and J2 bits. */ if (globals->use_rel) { bfd_vma s = (upper_insn & (1 << 10)) >> 10; bfd_vma upper = upper_insn & 0x3ff; bfd_vma lower = lower_insn & 0x7ff; bfd_vma j1 = (lower_insn & (1 << 13)) >> 13; bfd_vma j2 = (lower_insn & (1 << 11)) >> 11; bfd_vma i1 = j1 ^ s ? 0 : 1; bfd_vma i2 = j2 ^ s ? 0 : 1; addend = (i1 << 23) | (i2 << 22) | (upper << 12) | (lower << 1); /* Sign extend. */ addend = (addend | ((s ? 0 : 1) << 24)) - (1 << 24); signed_addend = addend; } if (r_type == R_ARM_THM_XPC22) { /* Check for Thumb to Thumb call. */ /* FIXME: Should we translate the instruction into a BL instruction instead ? */ if (sym_flags == STT_ARM_TFUNC) (*_bfd_error_handler) (_("%B: Warning: Thumb BLX instruction targets thumb function '%s'."), input_bfd, h ? h->root.root.string : "(local)"); } else { /* If it is not a call to Thumb, assume call to Arm. If it is a call relative to a section name, then it is not a function call at all, but rather a long jump. Calls through the PLT do not require stubs. */ if (sym_flags != STT_ARM_TFUNC && sym_flags != STT_SECTION && (h == NULL || splt == NULL || h->plt.offset == (bfd_vma) -1)) { if (globals->use_blx) { /* Convert BL to BLX. */ lower_insn = (lower_insn & ~0x1000) | 0x0800; } else if (elf32_thumb_to_arm_stub (info, sym_name, input_bfd, output_bfd, input_section, hit_data, sym_sec, rel->r_offset, signed_addend, value, error_message)) return bfd_reloc_ok; else return bfd_reloc_dangerous; } else if (sym_flags == STT_ARM_TFUNC && globals->use_blx) { /* Make sure this is a BL. */ lower_insn |= 0x1800; } } /* Handle calls via the PLT. */ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) { value = (splt->output_section->vma + splt->output_offset + h->plt.offset); if (globals->use_blx) { /* If the Thumb BLX instruction is available, convert the BL to a BLX instruction to call the ARM-mode PLT entry. */ lower_insn = (lower_insn & ~0x1000) | 0x0800; } else /* Target the Thumb stub before the ARM PLT entry. */ value -= PLT_THUMB_STUB_SIZE; *unresolved_reloc_p = FALSE; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); check = relocation >> howto->rightshift; /* If this is a signed value, the rightshift just dropped leading 1 bits (assuming twos complement). */ if ((bfd_signed_vma) relocation >= 0) signed_check = check; else signed_check = check | ~((bfd_vma) -1 >> howto->rightshift); /* Calculate the permissable maximum and minimum values for this relocation according to whether we're relocating for Thumb-2 or not. */ bitsize = howto->bitsize; if (!thumb2) bitsize -= 2; reloc_signed_max = ((1 << (bitsize - 1)) - 1) >> howto->rightshift; reloc_signed_min = ~reloc_signed_max; /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; if ((lower_insn & 0x1800) == 0x0800) /* For a BLX instruction, make sure that the relocation is rounded up to a word boundary. This follows the semantics of the instruction which specifies that bit 1 of the target address will come from bit 1 of the base address. */ relocation = (relocation + 2) & ~ 3; /* Put RELOCATION back into the insn. Assumes two's complement. We use the Thumb-2 encoding, which is safe even if dealing with a Thumb-1 instruction by virtue of our overflow check above. */ reloc_sign = (signed_check < 0) ? 1 : 0; upper_insn = (upper_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 12) & 0x3ff) | (reloc_sign << 10); lower_insn = (lower_insn & ~(bfd_vma) 0x2fff) | (((!((relocation >> 23) & 1)) ^ reloc_sign) << 13) | (((!((relocation >> 22) & 1)) ^ reloc_sign) << 11) | ((relocation >> 1) & 0x7ff); /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } break; case R_ARM_THM_JUMP24: /* Thumb32 unconditional branch instruction. */ { bfd_vma relocation; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max = ((1 << (howto->bitsize - 1)) - 1) >> howto->rightshift; bfd_signed_vma reloc_signed_min = ~ reloc_signed_max; bfd_vma check; bfd_signed_vma signed_check; /* Need to refetch the addend, reconstruct the top three bits, and glue the two pieces together. */ if (globals->use_rel) { bfd_vma S = (upper_insn & 0x0400) >> 10; bfd_vma hi = (upper_insn & 0x03ff); bfd_vma I1 = (lower_insn & 0x2000) >> 13; bfd_vma I2 = (lower_insn & 0x0800) >> 11; bfd_vma lo = (lower_insn & 0x07ff); I1 = !(I1 ^ S); I2 = !(I2 ^ S); S = !S; signed_addend = (S << 24) | (I1 << 23) | (I2 << 22) | (hi << 12) | (lo << 1); signed_addend -= (1 << 24); /* Sign extend. */ } /* ??? Should handle interworking? GCC might someday try to use this for tail calls. */ relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); check = relocation >> howto->rightshift; /* If this is a signed value, the rightshift just dropped leading 1 bits (assuming twos complement). */ if ((bfd_signed_vma) relocation >= 0) signed_check = check; else signed_check = check | ~((bfd_vma) -1 >> howto->rightshift); /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; /* Put RELOCATION back into the insn. */ { bfd_vma S = (relocation & 0x01000000) >> 24; bfd_vma I1 = (relocation & 0x00800000) >> 23; bfd_vma I2 = (relocation & 0x00400000) >> 22; bfd_vma hi = (relocation & 0x003ff000) >> 12; bfd_vma lo = (relocation & 0x00000ffe) >> 1; I1 = !(I1 ^ S); I2 = !(I2 ^ S); upper_insn = (upper_insn & (bfd_vma) 0xf800) | (S << 10) | hi; lower_insn = (lower_insn & (bfd_vma) 0xd000) | (I1 << 13) | (I2 << 11) | lo; } /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } case R_ARM_THM_JUMP19: /* Thumb32 conditional branch instruction. */ { bfd_vma relocation; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max = 0xffffe; bfd_signed_vma reloc_signed_min = -0x100000; bfd_signed_vma signed_check; /* Need to refetch the addend, reconstruct the top three bits, and squish the two 11 bit pieces together. */ if (globals->use_rel) { bfd_vma S = (upper_insn & 0x0400) >> 10; bfd_vma upper = (upper_insn & 0x003f); bfd_vma J1 = (lower_insn & 0x2000) >> 13; bfd_vma J2 = (lower_insn & 0x0800) >> 11; bfd_vma lower = (lower_insn & 0x07ff); upper |= J1 << 6; upper |= J2 << 7; upper |= (!S) << 8; upper -= 0x0100; /* Sign extend. */ addend = (upper << 12) | (lower << 1); signed_addend = addend; } /* ??? Should handle interworking? GCC might someday try to use this for tail calls. */ relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); signed_check = (bfd_signed_vma) relocation; if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; /* Put RELOCATION back into the insn. */ { bfd_vma S = (relocation & 0x00100000) >> 20; bfd_vma J2 = (relocation & 0x00080000) >> 19; bfd_vma J1 = (relocation & 0x00040000) >> 18; bfd_vma hi = (relocation & 0x0003f000) >> 12; bfd_vma lo = (relocation & 0x00000ffe) >> 1; upper_insn = (upper_insn & 0xfbc0) | (S << 10) | hi; lower_insn = (lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | lo; } /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } case R_ARM_THM_JUMP11: case R_ARM_THM_JUMP8: case R_ARM_THM_JUMP6: /* Thumb B (branch) instruction). */ { bfd_signed_vma relocation; bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1; bfd_signed_vma reloc_signed_min = ~ reloc_signed_max; bfd_signed_vma signed_check; /* CZB cannot jump backward. */ if (r_type == R_ARM_THM_JUMP6) reloc_signed_min = 0; if (globals->use_rel) { /* Need to refetch addend. */ addend = bfd_get_16 (input_bfd, hit_data) & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { signed_addend = -1; signed_addend &= ~ howto->src_mask; signed_addend |= addend; } else signed_addend = addend; /* The value in the insn has been right shifted. We need to undo this, so that we can perform the address calculation in terms of bytes. */ signed_addend <<= howto->rightshift; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); relocation >>= howto->rightshift; signed_check = relocation; if (r_type == R_ARM_THM_JUMP6) relocation = ((relocation & 0x0020) << 4) | ((relocation & 0x001f) << 3); else relocation &= howto->dst_mask; relocation |= (bfd_get_16 (input_bfd, hit_data) & (~ howto->dst_mask)); bfd_put_16 (input_bfd, relocation, hit_data); /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) return bfd_reloc_overflow; return bfd_reloc_ok; } case R_ARM_ALU_PCREL7_0: case R_ARM_ALU_PCREL15_8: case R_ARM_ALU_PCREL23_15: { bfd_vma insn; bfd_vma relocation; insn = bfd_get_32 (input_bfd, hit_data); if (globals->use_rel) { /* Extract the addend. */ addend = (insn & 0xff) << ((insn & 0xf00) >> 7); signed_addend = addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); insn = (insn & ~0xfff) | ((howto->bitpos << 7) & 0xf00) | ((relocation >> howto->bitpos) & 0xff); bfd_put_32 (input_bfd, value, hit_data); } return bfd_reloc_ok; case R_ARM_GNU_VTINHERIT: case R_ARM_GNU_VTENTRY: return bfd_reloc_ok; case R_ARM_GOTOFF32: /* Relocation is relative to the start of the global offset table. */ BFD_ASSERT (sgot != NULL); if (sgot == NULL) return bfd_reloc_notsupported; /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value += 1; /* Note that sgot->output_offset is not involved in this calculation. We always want the start of .got. If we define _GLOBAL_OFFSET_TABLE in a different way, as is permitted by the ABI, we might have to change this calculation. */ value -= sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_GOTPC: /* Use global offset table as symbol value. */ BFD_ASSERT (sgot != NULL); if (sgot == NULL) return bfd_reloc_notsupported; *unresolved_reloc_p = FALSE; value = sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_GOT32: case R_ARM_GOT_PREL: /* Relocation is to the entry for this symbol in the global offset table. */ if (sgot == NULL) return bfd_reloc_notsupported; if (h != NULL) { bfd_vma off; bfd_boolean dyn; off = h->got.offset; BFD_ASSERT (off != (bfd_vma) -1); dyn = globals->root.dynamic_sections_created; if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) || (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) || (ELF_ST_VISIBILITY (h->other) && h->root.type == bfd_link_hash_undefweak)) { /* This is actually a static link, or it is a -Bsymbolic link and the symbol is defined locally. We must initialize this entry in the global offset table. Since the offset must always be a multiple of 4, we use the least significant bit to record whether we have initialized it already. When doing a dynamic link, we create a .rel(a).got relocation entry to initialize the value. This is done in the finish_dynamic_symbol routine. */ if ((off & 1) != 0) off &= ~1; else { /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; bfd_put_32 (output_bfd, value, sgot->contents + off); h->got.offset |= 1; } } else *unresolved_reloc_p = FALSE; value = sgot->output_offset + off; } else { bfd_vma off; BFD_ASSERT (local_got_offsets != NULL && local_got_offsets[r_symndx] != (bfd_vma) -1); off = local_got_offsets[r_symndx]; /* The offset must always be a multiple of 4. We use the least significant bit to record whether we have already generated the necessary reloc. */ if ((off & 1) != 0) off &= ~1; else { /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; if (globals->use_rel) bfd_put_32 (output_bfd, value, sgot->contents + off); if (info->shared) { asection * srelgot; Elf_Internal_Rela outrel; bfd_byte *loc; srelgot = (bfd_get_section_by_name (dynobj, RELOC_SECTION (globals, ".got"))); BFD_ASSERT (srelgot != NULL); outrel.r_addend = addend + value; outrel.r_offset = (sgot->output_section->vma + sgot->output_offset + off); outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE); loc = srelgot->contents; loc += srelgot->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); } local_got_offsets[r_symndx] |= 1; } value = sgot->output_offset + off; } if (r_type != R_ARM_GOT32) value += sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_TLS_LDO32: value = value - dtpoff_base (info); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_TLS_LDM32: { bfd_vma off; if (globals->sgot == NULL) abort (); off = globals->tls_ldm_got.offset; if ((off & 1) != 0) off &= ~1; else { /* If we don't know the module number, create a relocation for it. */ if (info->shared) { Elf_Internal_Rela outrel; bfd_byte *loc; if (globals->srelgot == NULL) abort (); outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + off); outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + off); loc = globals->srelgot->contents; loc += globals->srelgot->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); } else bfd_put_32 (output_bfd, 1, globals->sgot->contents + off); globals->tls_ldm_got.offset |= 1; } value = globals->sgot->output_section->vma + globals->sgot->output_offset + off - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: { bfd_vma off; int indx; char tls_type; if (globals->sgot == NULL) abort (); indx = 0; if (h != NULL) { bfd_boolean dyn; dyn = globals->root.dynamic_sections_created; if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h))) { *unresolved_reloc_p = FALSE; indx = h->dynindx; } off = h->got.offset; tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type; } else { if (local_got_offsets == NULL) abort (); off = local_got_offsets[r_symndx]; tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx]; } if (tls_type == GOT_UNKNOWN) abort (); if ((off & 1) != 0) off &= ~1; else { bfd_boolean need_relocs = FALSE; Elf_Internal_Rela outrel; bfd_byte *loc = NULL; int cur_off = off; /* The GOT entries have not been initialized yet. Do it now, and emit any relocations. If both an IE GOT and a GD GOT are necessary, we emit the GD first. */ if ((info->shared || indx != 0) && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) { need_relocs = TRUE; if (globals->srelgot == NULL) abort (); loc = globals->srelgot->contents; loc += globals->srelgot->reloc_count * RELOC_SIZE (globals); } if (tls_type & GOT_TLS_GD) { if (need_relocs) { outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + cur_off); outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); if (indx == 0) bfd_put_32 (output_bfd, value - dtpoff_base (info), globals->sgot->contents + cur_off + 4); else { outrel.r_addend = 0; outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPOFF32); outrel.r_offset += 4; if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off + 4); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); } } else { /* If we are not emitting relocations for a general dynamic reference, then we must be in a static link or an executable link with the symbol binding locally. Mark it as belonging to module 1, the executable. */ bfd_put_32 (output_bfd, 1, globals->sgot->contents + cur_off); bfd_put_32 (output_bfd, value - dtpoff_base (info), globals->sgot->contents + cur_off + 4); } cur_off += 8; } if (tls_type & GOT_TLS_IE) { if (need_relocs) { if (indx == 0) outrel.r_addend = value - dtpoff_base (info); else outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + cur_off); outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); } else bfd_put_32 (output_bfd, tpoff (info, value), globals->sgot->contents + cur_off); cur_off += 4; } if (h != NULL) h->got.offset |= 1; else local_got_offsets[r_symndx] |= 1; } if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32) off += 8; value = globals->sgot->output_section->vma + globals->sgot->output_offset + off - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } case R_ARM_TLS_LE32: if (info->shared) { (*_bfd_error_handler) (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"), input_bfd, input_section, (long) rel->r_offset, howto->name); return FALSE; } else value = tpoff (info, value); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_V4BX: if (globals->fix_v4bx) { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); /* Ensure that we have a BX instruction. */ BFD_ASSERT ((insn & 0x0ffffff0) == 0x012fff10); /* Preserve Rm (lowest four bits) and the condition code (highest four bits). Other bits encode MOV PC,Rm. */ insn = (insn & 0xf000000f) | 0x01a0f000; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: /* Until we properly support segment-base-relative addressing then we assume the segment base to be zero, as for the group relocations. Thus R_ARM_MOVW_BREL_NC has the same semantics as R_ARM_MOVW_ABS_NC and R_ARM_MOVT_BREL has the same semantics as R_ARM_MOVT_ABS. */ case R_ARM_MOVW_BREL_NC: case R_ARM_MOVW_BREL: case R_ARM_MOVT_BREL: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); if (globals->use_rel) { addend = ((insn >> 4) & 0xf000) | (insn & 0xfff); signed_addend = (addend ^ 0x8000) - 0x8000; } value += signed_addend; if (r_type == R_ARM_MOVW_PREL_NC || r_type == R_ARM_MOVT_PREL) value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); if (r_type == R_ARM_MOVW_BREL && value >= 0x10000) return bfd_reloc_overflow; if (sym_flags == STT_ARM_TFUNC) value |= 1; if (r_type == R_ARM_MOVT_ABS || r_type == R_ARM_MOVT_PREL || r_type == R_ARM_MOVT_BREL) value >>= 16; insn &= 0xfff0f000; insn |= value & 0xfff; insn |= (value & 0xf000) << 4; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Until we properly support segment-base-relative addressing then we assume the segment base to be zero, as for the above relocations. Thus R_ARM_THM_MOVW_BREL_NC has the same semantics as R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_BREL has the same semantics as R_ARM_THM_MOVT_ABS. */ case R_ARM_THM_MOVW_BREL_NC: case R_ARM_THM_MOVW_BREL: case R_ARM_THM_MOVT_BREL: { bfd_vma insn; insn = bfd_get_16 (input_bfd, hit_data) << 16; insn |= bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { addend = ((insn >> 4) & 0xf000) | ((insn >> 15) & 0x0800) | ((insn >> 4) & 0x0700) | (insn & 0x00ff); signed_addend = (addend ^ 0x10000) - 0x10000; } value += signed_addend; if (r_type == R_ARM_THM_MOVW_PREL_NC || r_type == R_ARM_THM_MOVT_PREL) value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); if (r_type == R_ARM_THM_MOVW_BREL && value >= 0x10000) return bfd_reloc_overflow; if (sym_flags == STT_ARM_TFUNC) value |= 1; if (r_type == R_ARM_THM_MOVT_ABS || r_type == R_ARM_THM_MOVT_PREL || r_type == R_ARM_THM_MOVT_BREL) value >>= 16; insn &= 0xfbf08f00; insn |= (value & 0xf000) << 4; insn |= (value & 0x0800) << 15; insn |= (value & 0x0700) << 4; insn |= (value & 0x00ff); bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); } return bfd_reloc_ok; case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_PC_G1: case R_ARM_ALU_PC_G2: case R_ARM_ALU_SB_G0_NC: case R_ARM_ALU_SB_G1_NC: case R_ARM_ALU_SB_G0: case R_ARM_ALU_SB_G1: case R_ARM_ALU_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; /* sb should be the origin of the *segment* containing the symbol. It is not clear how to obtain this OS-dependent value, so we make an arbitrary choice of zero. */ bfd_vma sb = 0; bfd_vma residual; bfd_vma g_n; bfd_signed_vma signed_value; int group = 0; /* Determine which group of bits to select. */ switch (r_type) { case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_SB_G0_NC: case R_ARM_ALU_SB_G0: group = 0; break; case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G1: case R_ARM_ALU_SB_G1_NC: case R_ARM_ALU_SB_G1: group = 1; break; case R_ARM_ALU_PC_G2: case R_ARM_ALU_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative; bfd_vma constant = insn & 0xff; bfd_vma rotation = (insn & 0xf00) >> 8; if (rotation == 0) signed_addend = constant; else { /* Compensate for the fact that in the instruction, the rotation is stored in multiples of 2 bits. */ rotation *= 2; /* Rotate "constant" right by "rotation" bits. */ signed_addend = (constant >> rotation) | (constant << (8 * sizeof (bfd_vma) - rotation)); } /* Determine if the instruction is an ADD or a SUB. (For REL, this determines the sign of the addend.) */ negative = identify_add_or_sub (insn); if (negative == 0) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group relocations"), input_bfd, input_section, (long) rel->r_offset, howto->name); return bfd_reloc_overflow; } signed_addend *= negative; } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_ALU_PC_G0_NC || r_type == R_ARM_ALU_PC_G1_NC || r_type == R_ARM_ALU_PC_G0 || r_type == R_ARM_ALU_PC_G1 || r_type == R_ARM_ALU_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* If the target symbol is a Thumb function, then set the Thumb bit in the address. */ if (sym_flags == STT_ARM_TFUNC) signed_value |= 1; /* Calculate the value of the relevant G_n, in encoded constant-with-rotation format. */ g_n = calculate_group_reloc_mask (llabs (signed_value), group, &residual); /* Check for overflow if required. */ if ((r_type == R_ARM_ALU_PC_G0 || r_type == R_ARM_ALU_PC_G1 || r_type == R_ARM_ALU_PC_G2 || r_type == R_ARM_ALU_SB_G0 || r_type == R_ARM_ALU_SB_G1 || r_type == R_ARM_ALU_SB_G2) && residual != 0) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and the ADD/SUB part of the opcode; take care not to destroy the S bit. */ insn &= 0xff1ff000; /* Set the opcode according to whether the value to go in the place is negative. */ if (signed_value < 0) insn |= 1 << 22; else insn |= 1 << 23; /* Encode the offset. */ insn |= g_n; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDR_PC_G0: case R_ARM_LDR_PC_G1: case R_ARM_LDR_PC_G2: case R_ARM_LDR_SB_G0: case R_ARM_LDR_SB_G1: case R_ARM_LDR_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDR_PC_G0: case R_ARM_LDR_SB_G0: group = 0; break; case R_ARM_LDR_PC_G1: case R_ARM_LDR_SB_G1: group = 1; break; case R_ARM_LDR_PC_G2: case R_ARM_LDR_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * (insn & 0xfff); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDR_PC_G0 || r_type == R_ARM_LDR_PC_G1 || r_type == R_ARM_LDR_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. */ if (residual >= 0x1000) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7ff000; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= residual; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_PC_G2: case R_ARM_LDRS_SB_G0: case R_ARM_LDRS_SB_G1: case R_ARM_LDRS_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_SB_G0: group = 0; break; case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_SB_G1: group = 1; break; case R_ARM_LDRS_PC_G2: case R_ARM_LDRS_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * (((insn & 0xf00) >> 4) + (insn & 0xf)); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDRS_PC_G0 || r_type == R_ARM_LDRS_PC_G1 || r_type == R_ARM_LDRS_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. */ if (residual >= 0x100) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7ff0f0; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= ((residual & 0xf0) << 4) | (residual & 0xf); bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDC_PC_G0: case R_ARM_LDC_PC_G1: case R_ARM_LDC_PC_G2: case R_ARM_LDC_SB_G0: case R_ARM_LDC_SB_G1: case R_ARM_LDC_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDC_PC_G0: case R_ARM_LDC_SB_G0: group = 0; break; case R_ARM_LDC_PC_G1: case R_ARM_LDC_SB_G1: group = 1; break; case R_ARM_LDC_PC_G2: case R_ARM_LDC_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * ((insn & 0xff) << 2); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDC_PC_G0 || r_type == R_ARM_LDC_PC_G1 || r_type == R_ARM_LDC_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. (The absolute value to go in the place must be divisible by four and, after having been divided by four, must fit in eight bits.) */ if ((residual & 0x3) != 0 || residual >= 0x400) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7fff00; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= residual >> 2; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; default: return bfd_reloc_notsupported; } } /* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS. */ static void arm_add_to_rel (bfd * abfd, bfd_byte * address, reloc_howto_type * howto, bfd_signed_vma increment) { bfd_signed_vma addend; if (howto->type == R_ARM_THM_CALL) { int upper_insn, lower_insn; int upper, lower; upper_insn = bfd_get_16 (abfd, address); lower_insn = bfd_get_16 (abfd, address + 2); upper = upper_insn & 0x7ff; lower = lower_insn & 0x7ff; addend = (upper << 12) | (lower << 1); addend += increment; addend >>= 1; upper_insn = (upper_insn & 0xf800) | ((addend >> 11) & 0x7ff); lower_insn = (lower_insn & 0xf800) | (addend & 0x7ff); bfd_put_16 (abfd, (bfd_vma) upper_insn, address); bfd_put_16 (abfd, (bfd_vma) lower_insn, address + 2); } else { bfd_vma contents; contents = bfd_get_32 (abfd, address); /* Get the (signed) value from the instruction. */ addend = contents & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { bfd_signed_vma mask; mask = -1; mask &= ~ howto->src_mask; addend |= mask; } /* Add in the increment, (which is a byte value). */ switch (howto->type) { default: addend += increment; break; case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: addend <<= howto->size; addend += increment; /* Should we check for overflow here ? */ /* Drop any undesired bits. */ addend >>= howto->rightshift; break; } contents = (contents & ~ howto->dst_mask) | (addend & howto->dst_mask); bfd_put_32 (abfd, contents, address); } } #define IS_ARM_TLS_RELOC(R_TYPE) \ ((R_TYPE) == R_ARM_TLS_GD32 \ || (R_TYPE) == R_ARM_TLS_LDO32 \ || (R_TYPE) == R_ARM_TLS_LDM32 \ || (R_TYPE) == R_ARM_TLS_DTPOFF32 \ || (R_TYPE) == R_ARM_TLS_DTPMOD32 \ || (R_TYPE) == R_ARM_TLS_TPOFF32 \ || (R_TYPE) == R_ARM_TLS_LE32 \ || (R_TYPE) == R_ARM_TLS_IE32) /* Relocate an ARM ELF section. */ static bfd_boolean elf32_arm_relocate_section (bfd * output_bfd, struct bfd_link_info * info, bfd * input_bfd, asection * input_section, bfd_byte * contents, Elf_Internal_Rela * relocs, Elf_Internal_Sym * local_syms, asection ** local_sections) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; Elf_Internal_Rela *rel; Elf_Internal_Rela *relend; const char *name; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); rel = relocs; relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) { int r_type; reloc_howto_type * howto; unsigned long r_symndx; Elf_Internal_Sym * sym; asection * sec; struct elf_link_hash_entry * h; bfd_vma relocation; bfd_reloc_status_type r; arelent bfd_reloc; char sym_type; bfd_boolean unresolved_reloc = FALSE; char *error_message = NULL; r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (globals, r_type); if ( r_type == R_ARM_GNU_VTENTRY || r_type == R_ARM_GNU_VTINHERIT) continue; bfd_reloc.howto = elf32_arm_howto_from_type (r_type); howto = bfd_reloc.howto; h = NULL; sym = NULL; sec = NULL; if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; sym_type = ELF32_ST_TYPE (sym->st_info); sec = local_sections[r_symndx]; if (globals->use_rel) { relocation = (sec->output_section->vma + sec->output_offset + sym->st_value); if (!info->relocatable && (sec->flags & SEC_MERGE) && ELF_ST_TYPE (sym->st_info) == STT_SECTION) { asection *msec; bfd_vma addend, value; if (howto->rightshift) { (*_bfd_error_handler) (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"), input_bfd, input_section, (long) rel->r_offset, howto->name); return FALSE; } value = bfd_get_32 (input_bfd, contents + rel->r_offset); /* Get the (signed) value from the instruction. */ addend = value & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { bfd_signed_vma mask; mask = -1; mask &= ~ howto->src_mask; addend |= mask; } msec = sec; addend = _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend) - relocation; addend += msec->output_section->vma + msec->output_offset; value = (value & ~ howto->dst_mask) | (addend & howto->dst_mask); bfd_put_32 (input_bfd, value, contents + rel->r_offset); } } else relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); } else { bfd_boolean warned; RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, r_symndx, symtab_hdr, sym_hashes, h, sec, relocation, unresolved_reloc, warned); sym_type = h->type; } if (sec != NULL && elf_discarded_section (sec)) { /* For relocs against symbols from removed linkonce sections, or sections discarded by a linker script, we just want the section contents zeroed. Avoid any special processing. */ _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); rel->r_info = 0; rel->r_addend = 0; continue; } if (info->relocatable) { /* This is a relocatable link. We don't have to change anything, unless the reloc is against a section symbol, in which case we have to adjust according to where the section symbol winds up in the output section. */ if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) { if (globals->use_rel) arm_add_to_rel (input_bfd, contents + rel->r_offset, howto, (bfd_signed_vma) sec->output_offset); else rel->r_addend += sec->output_offset; } continue; } if (h != NULL) name = h->root.root.string; else { name = (bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name)); if (name == NULL || *name == '\0') name = bfd_section_name (input_bfd, sec); } if (r_symndx != 0 && r_type != R_ARM_NONE && (h == NULL || h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && IS_ARM_TLS_RELOC (r_type) != (sym_type == STT_TLS)) { (*_bfd_error_handler) ((sym_type == STT_TLS ? _("%B(%A+0x%lx): %s used with TLS symbol %s") : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")), input_bfd, input_section, (long) rel->r_offset, howto->name, name); } r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd, input_section, contents, rel, relocation, info, sec, name, (h ? ELF_ST_TYPE (h->type) : ELF_ST_TYPE (sym->st_info)), h, &unresolved_reloc, &error_message); /* Dynamic relocs are not propagated for SEC_DEBUGGING sections because such sections are not SEC_ALLOC and thus ld.so will not process them. */ if (unresolved_reloc && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic)) { (*_bfd_error_handler) (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"), input_bfd, input_section, (long) rel->r_offset, howto->name, h->root.root.string); return FALSE; } if (r != bfd_reloc_ok) { switch (r) { case bfd_reloc_overflow: /* If the overflowing reloc was to an undefined symbol, we have already printed one error message and there is no point complaining again. */ if ((! h || h->root.type != bfd_link_hash_undefined) && (!((*info->callbacks->reloc_overflow) (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0, input_bfd, input_section, rel->r_offset)))) return FALSE; break; case bfd_reloc_undefined: if (!((*info->callbacks->undefined_symbol) (info, name, input_bfd, input_section, rel->r_offset, TRUE))) return FALSE; break; case bfd_reloc_outofrange: error_message = _("out of range"); goto common_error; case bfd_reloc_notsupported: error_message = _("unsupported relocation"); goto common_error; case bfd_reloc_dangerous: /* error_message should already be set. */ goto common_error; default: error_message = _("unknown error"); /* fall through */ common_error: BFD_ASSERT (error_message != NULL); if (!((*info->callbacks->reloc_dangerous) (info, error_message, input_bfd, input_section, rel->r_offset))) return FALSE; break; } } } return TRUE; } /* Set the right machine number. */ static bfd_boolean elf32_arm_object_p (bfd *abfd) { unsigned int mach; mach = bfd_arm_get_mach_from_notes (abfd, ARM_NOTE_SECTION); if (mach != bfd_mach_arm_unknown) bfd_default_set_arch_mach (abfd, bfd_arch_arm, mach); else if (elf_elfheader (abfd)->e_flags & EF_ARM_MAVERICK_FLOAT) bfd_default_set_arch_mach (abfd, bfd_arch_arm, bfd_mach_arm_ep9312); else bfd_default_set_arch_mach (abfd, bfd_arch_arm, mach); return TRUE; } /* Function to keep ARM specific flags in the ELF header. */ static bfd_boolean elf32_arm_set_private_flags (bfd *abfd, flagword flags) { if (elf_flags_init (abfd) && elf_elfheader (abfd)->e_flags != flags) { if (EF_ARM_EABI_VERSION (flags) == EF_ARM_EABI_UNKNOWN) { if (flags & EF_ARM_INTERWORK) (*_bfd_error_handler) (_("Warning: Not setting interworking flag of %B since it has already been specified as non-interworking"), abfd); else _bfd_error_handler (_("Warning: Clearing the interworking flag of %B due to outside request"), abfd); } } else { elf_elfheader (abfd)->e_flags = flags; elf_flags_init (abfd) = TRUE; } return TRUE; } /* Copy backend specific data from one object module to another. */ static bfd_boolean elf32_arm_copy_private_bfd_data (bfd *ibfd, bfd *obfd) { flagword in_flags; flagword out_flags; if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour || bfd_get_flavour (obfd) != bfd_target_elf_flavour) return TRUE; in_flags = elf_elfheader (ibfd)->e_flags; out_flags = elf_elfheader (obfd)->e_flags; if (elf_flags_init (obfd) && EF_ARM_EABI_VERSION (out_flags) == EF_ARM_EABI_UNKNOWN && in_flags != out_flags) { /* Cannot mix APCS26 and APCS32 code. */ if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26)) return FALSE; /* Cannot mix float APCS and non-float APCS code. */ if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT)) return FALSE; /* If the src and dest have different interworking flags then turn off the interworking bit. */ if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK)) { if (out_flags & EF_ARM_INTERWORK) _bfd_error_handler (_("Warning: Clearing the interworking flag of %B because non-interworking code in %B has been linked with it"), obfd, ibfd); in_flags &= ~EF_ARM_INTERWORK; } /* Likewise for PIC, though don't warn for this case. */ if ((in_flags & EF_ARM_PIC) != (out_flags & EF_ARM_PIC)) in_flags &= ~EF_ARM_PIC; } elf_elfheader (obfd)->e_flags = in_flags; elf_flags_init (obfd) = TRUE; /* Also copy the EI_OSABI field. */ elf_elfheader (obfd)->e_ident[EI_OSABI] = elf_elfheader (ibfd)->e_ident[EI_OSABI]; /* Copy object attributes. */ _bfd_elf_copy_obj_attributes (ibfd, obfd); return TRUE; } /* Values for Tag_ABI_PCS_R9_use. */ enum { AEABI_R9_V6, AEABI_R9_SB, AEABI_R9_TLS, AEABI_R9_unused }; /* Values for Tag_ABI_PCS_RW_data. */ enum { AEABI_PCS_RW_data_absolute, AEABI_PCS_RW_data_PCrel, AEABI_PCS_RW_data_SBrel, AEABI_PCS_RW_data_unused }; /* Values for Tag_ABI_enum_size. */ enum { AEABI_enum_unused, AEABI_enum_short, AEABI_enum_wide, AEABI_enum_forced_wide }; /* Determine whether an object attribute tag takes an integer, a string or both. */ static int elf32_arm_obj_attrs_arg_type (int tag) { if (tag == Tag_compatibility) return 3; else if (tag == 4 || tag == 5) return 2; else if (tag < 32) return 1; else return (tag & 1) != 0 ? 2 : 1; } /* Merge EABI object attributes from IBFD into OBFD. Raise an error if there are conflicting attributes. */ static bfd_boolean elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd) { obj_attribute *in_attr; obj_attribute *out_attr; obj_attribute_list *in_list; /* Some tags have 0 = don't care, 1 = strong requirement, 2 = weak requirement. */ static const int order_312[3] = {3, 1, 2}; int i; if (!elf_known_obj_attributes_proc (obfd)[0].i) { /* This is the first object. Copy the attributes. */ _bfd_elf_copy_obj_attributes (ibfd, obfd); /* Use the Tag_null value to indicate the attributes have been initialized. */ elf_known_obj_attributes_proc (obfd)[0].i = 1; return TRUE; } in_attr = elf_known_obj_attributes_proc (ibfd); out_attr = elf_known_obj_attributes_proc (obfd); /* This needs to happen before Tag_ABI_FP_number_model is merged. */ if (in_attr[Tag_ABI_VFP_args].i != out_attr[Tag_ABI_VFP_args].i) { /* Ignore mismatches if teh object doesn't use floating point. */ if (out_attr[Tag_ABI_FP_number_model].i == 0) out_attr[Tag_ABI_VFP_args].i = in_attr[Tag_ABI_VFP_args].i; else if (in_attr[Tag_ABI_FP_number_model].i != 0) { bfd *hasbfd, *hasnotbfd; if (in_attr[Tag_ABI_VFP_args].i) { hasbfd = ibfd; hasnotbfd = obfd; } else { hasbfd = obfd; hasnotbfd = ibfd; } _bfd_error_handler (_("ERROR: %B uses VFP register arguments, %B does not"), hasbfd, hasnotbfd); return FALSE; } } for (i = 4; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++) { if (out_attr[i].type == 0) out_attr[i].type = in_attr[i].type; /* Merge this attribute with existing attributes. */ switch (i) { case Tag_CPU_raw_name: case Tag_CPU_name: /* Use whichever has the greatest architecture requirements. We won't necessarily have both the above tags, so make sure input name is non-NULL. */ if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i && in_attr[i].s) out_attr[i].s = _bfd_elf_attr_strdup (obfd, in_attr[i].s); break; case Tag_ABI_optimization_goals: case Tag_ABI_FP_optimization_goals: /* Use the first value seen. */ break; case Tag_CPU_arch: case Tag_ARM_ISA_use: case Tag_THUMB_ISA_use: case Tag_VFP_arch: case Tag_WMMX_arch: case Tag_NEON_arch: /* ??? Do NEON and WMMX conflict? */ case Tag_ABI_FP_rounding: case Tag_ABI_FP_denormal: case Tag_ABI_FP_exceptions: case Tag_ABI_FP_user_exceptions: case Tag_ABI_FP_number_model: case Tag_ABI_align8_preserved: case Tag_ABI_HardFP_use: case Tag_CPU_unaligned_access: case Tag_FP_HP_extension: /* Use the largest value specified. */ if (in_attr[i].i > out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_CPU_arch_profile: /* Warn if conflicting architecture profiles used. */ if (out_attr[i].i && in_attr[i].i && in_attr[i].i != out_attr[i].i) { _bfd_error_handler (_("ERROR: %B: Conflicting architecture profiles %c/%c"), ibfd, in_attr[i].i, out_attr[i].i); return FALSE; } if (in_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_PCS_config: if (out_attr[i].i == 0) out_attr[i].i = in_attr[i].i; else if (in_attr[i].i != 0 && out_attr[i].i != 0) { /* It's sometimes ok to mix different configs, so this is only a warning. */ _bfd_error_handler (_("Warning: %B: Conflicting platform configuration"), ibfd); } break; case Tag_ABI_PCS_R9_use: if (in_attr[i].i != out_attr[i].i && out_attr[i].i != AEABI_R9_unused && in_attr[i].i != AEABI_R9_unused) { _bfd_error_handler (_("ERROR: %B: Conflicting use of R9"), ibfd); return FALSE; } if (out_attr[i].i == AEABI_R9_unused) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_RW_data: if (in_attr[i].i == AEABI_PCS_RW_data_SBrel && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_SB && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused) { _bfd_error_handler (_("ERROR: %B: SB relative addressing conflicts with use of R9"), ibfd); return FALSE; } /* Use the smallest value specified. */ if (in_attr[i].i < out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_RO_data: /* Use the smallest value specified. */ if (in_attr[i].i < out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_GOT_use: if (in_attr[i].i > 2 || out_attr[i].i > 2 || order_312[in_attr[i].i] < order_312[out_attr[i].i]) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_wchar_t: if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i) { _bfd_error_handler (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd); return FALSE; } if (in_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_align8_needed: /* ??? Check against Tag_ABI_align8_preserved. */ if (in_attr[i].i > 2 || out_attr[i].i > 2 || order_312[in_attr[i].i] < order_312[out_attr[i].i]) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_enum_size: if (in_attr[i].i != AEABI_enum_unused) { if (out_attr[i].i == AEABI_enum_unused || out_attr[i].i == AEABI_enum_forced_wide) { /* The existing object is compatible with anything. Use whatever requirements the new object has. */ out_attr[i].i = in_attr[i].i; } else if (in_attr[i].i != AEABI_enum_forced_wide && out_attr[i].i != in_attr[i].i && !elf32_arm_tdata (obfd)->no_enum_size_warning) { const char *aeabi_enum_names[] = { "", "variable-size", "32-bit", "" }; _bfd_error_handler (_("warning: %B uses %s enums yet the output is to use %s enums; use of enum values across objects may fail"), ibfd, aeabi_enum_names[in_attr[i].i], aeabi_enum_names[out_attr[i].i]); } } break; case Tag_ABI_VFP_args: /* Aready done. */ break; case Tag_ABI_WMMX_args: if (in_attr[i].i != out_attr[i].i) { _bfd_error_handler (_("ERROR: %B uses iWMMXt register arguments, %B does not"), ibfd, obfd); return FALSE; } break; default: /* All known attributes should be explicitly covered. */ /* XXX Not now */ /* abort (); */ break; } } /* Merge Tag_compatibility attributes and any common GNU ones. */ _bfd_elf_merge_object_attributes (ibfd, obfd); /* Check for any attributes not known on ARM. */ in_list = elf_other_obj_attributes_proc (ibfd); while (in_list && in_list->tag == Tag_compatibility) in_list = in_list->next; for (; in_list; in_list = in_list->next) { if ((in_list->tag & 128) < 64 && in_list->tag != Tag_Virtualization_use) { _bfd_error_handler (_("Warning: %B: Unknown EABI object attribute %d"), ibfd, in_list->tag); break; } } return TRUE; } /* Return TRUE if the two EABI versions are incompatible. */ static bfd_boolean elf32_arm_versions_compatible (unsigned iver, unsigned over) { /* v4 and v5 are the same spec before and after it was released, so allow mixing them. */ if ((iver == EF_ARM_EABI_VER4 && over == EF_ARM_EABI_VER5) || (iver == EF_ARM_EABI_VER5 && over == EF_ARM_EABI_VER4)) return TRUE; return (iver == over); } /* Merge backend specific data from an object file to the output object file when linking. */ static bfd_boolean elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd) { flagword out_flags; flagword in_flags; bfd_boolean flags_compatible = TRUE; asection *sec; /* Check if we have the same endianess. */ if (! _bfd_generic_verify_endian_match (ibfd, obfd)) return FALSE; if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour || bfd_get_flavour (obfd) != bfd_target_elf_flavour) return TRUE; if (!elf32_arm_merge_eabi_attributes (ibfd, obfd)) return FALSE; /* The input BFD must have had its flags initialised. */ /* The following seems bogus to me -- The flags are initialized in the assembler but I don't think an elf_flags_init field is written into the object. */ /* BFD_ASSERT (elf_flags_init (ibfd)); */ in_flags = elf_elfheader (ibfd)->e_flags; out_flags = elf_elfheader (obfd)->e_flags; if (!elf_flags_init (obfd)) { /* If the input is the default architecture and had the default flags then do not bother setting the flags for the output architecture, instead allow future merges to do this. If no future merges ever set these flags then they will retain their uninitialised values, which surprise surprise, correspond to the default values. */ if (bfd_get_arch_info (ibfd)->the_default && elf_elfheader (ibfd)->e_flags == 0) return TRUE; elf_flags_init (obfd) = TRUE; elf_elfheader (obfd)->e_flags = in_flags; if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) && bfd_get_arch_info (obfd)->the_default) return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd)); return TRUE; } /* Determine what should happen if the input ARM architecture does not match the output ARM architecture. */ if (! bfd_arm_merge_machines (ibfd, obfd)) return FALSE; /* Identical flags must be compatible. */ if (in_flags == out_flags) return TRUE; /* Check to see if the input BFD actually contains any sections. If not, its flags may not have been initialised either, but it cannot actually cause any incompatiblity. Do not short-circuit dynamic objects; their section list may be emptied by elf_link_add_object_symbols. Also check to see if there are no code sections in the input. In this case there is no need to check for code specific flags. XXX - do we need to worry about floating-point format compatability in data sections ? */ if (!(ibfd->flags & DYNAMIC)) { bfd_boolean null_input_bfd = TRUE; bfd_boolean only_data_sections = TRUE; for (sec = ibfd->sections; sec != NULL; sec = sec->next) { /* Ignore synthetic glue sections. */ if (strcmp (sec->name, ".glue_7") && strcmp (sec->name, ".glue_7t")) { if ((bfd_get_section_flags (ibfd, sec) & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) only_data_sections = FALSE; null_input_bfd = FALSE; break; } } if (null_input_bfd || only_data_sections) return TRUE; } /* Complain about various flag mismatches. */ if (!elf32_arm_versions_compatible (EF_ARM_EABI_VERSION (in_flags), EF_ARM_EABI_VERSION (out_flags))) { _bfd_error_handler (_("ERROR: Source object %B has EABI version %d, but target %B has EABI version %d"), ibfd, obfd, (in_flags & EF_ARM_EABIMASK) >> 24, (out_flags & EF_ARM_EABIMASK) >> 24); return FALSE; } /* Not sure what needs to be checked for EABI versions >= 1. */ /* VxWorks libraries do not use these flags. */ if (get_elf_backend_data (obfd) != &elf32_arm_vxworks_bed && get_elf_backend_data (ibfd) != &elf32_arm_vxworks_bed && EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN) { if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26)) { _bfd_error_handler (_("ERROR: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"), ibfd, obfd, in_flags & EF_ARM_APCS_26 ? 26 : 32, out_flags & EF_ARM_APCS_26 ? 26 : 32); flags_compatible = FALSE; } if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT)) { if (in_flags & EF_ARM_APCS_FLOAT) _bfd_error_handler (_("ERROR: %B passes floats in float registers, whereas %B passes them in integer registers"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B passes floats in integer registers, whereas %B passes them in float registers"), ibfd, obfd); flags_compatible = FALSE; } if ((in_flags & EF_ARM_VFP_FLOAT) != (out_flags & EF_ARM_VFP_FLOAT)) { if (in_flags & EF_ARM_VFP_FLOAT) _bfd_error_handler (_("ERROR: %B uses VFP instructions, whereas %B does not"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B uses FPA instructions, whereas %B does not"), ibfd, obfd); flags_compatible = FALSE; } if ((in_flags & EF_ARM_MAVERICK_FLOAT) != (out_flags & EF_ARM_MAVERICK_FLOAT)) { if (in_flags & EF_ARM_MAVERICK_FLOAT) _bfd_error_handler (_("ERROR: %B uses Maverick instructions, whereas %B does not"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B does not use Maverick instructions, whereas %B does"), ibfd, obfd); flags_compatible = FALSE; } #ifdef EF_ARM_SOFT_FLOAT if ((in_flags & EF_ARM_SOFT_FLOAT) != (out_flags & EF_ARM_SOFT_FLOAT)) { /* We can allow interworking between code that is VFP format layout, and uses either soft float or integer regs for passing floating point arguments and results. We already know that the APCS_FLOAT flags match; similarly for VFP flags. */ if ((in_flags & EF_ARM_APCS_FLOAT) != 0 || (in_flags & EF_ARM_VFP_FLOAT) == 0) { if (in_flags & EF_ARM_SOFT_FLOAT) _bfd_error_handler (_("ERROR: %B uses software FP, whereas %B uses hardware FP"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B uses hardware FP, whereas %B uses software FP"), ibfd, obfd); flags_compatible = FALSE; } } #endif /* Interworking mismatch is only a warning. */ if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK)) { if (in_flags & EF_ARM_INTERWORK) { _bfd_error_handler (_("Warning: %B supports interworking, whereas %B does not"), ibfd, obfd); } else { _bfd_error_handler (_("Warning: %B does not support interworking, whereas %B does"), ibfd, obfd); } } } return flags_compatible; } /* Display the flags field. */ static bfd_boolean elf32_arm_print_private_bfd_data (bfd *abfd, void * ptr) { FILE * file = (FILE *) ptr; unsigned long flags; BFD_ASSERT (abfd != NULL && ptr != NULL); /* Print normal ELF private data. */ _bfd_elf_print_private_bfd_data (abfd, ptr); flags = elf_elfheader (abfd)->e_flags; /* Ignore init flag - it may not be set, despite the flags field containing valid data. */ /* xgettext:c-format */ fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags); switch (EF_ARM_EABI_VERSION (flags)) { case EF_ARM_EABI_UNKNOWN: /* The following flag bits are GNU extensions and not part of the official ARM ELF extended ABI. Hence they are only decoded if the EABI version is not set. */ if (flags & EF_ARM_INTERWORK) fprintf (file, _(" [interworking enabled]")); if (flags & EF_ARM_APCS_26) fprintf (file, " [APCS-26]"); else fprintf (file, " [APCS-32]"); if (flags & EF_ARM_VFP_FLOAT) fprintf (file, _(" [VFP float format]")); else if (flags & EF_ARM_MAVERICK_FLOAT) fprintf (file, _(" [Maverick float format]")); else fprintf (file, _(" [FPA float format]")); if (flags & EF_ARM_APCS_FLOAT) fprintf (file, _(" [floats passed in float registers]")); if (flags & EF_ARM_PIC) fprintf (file, _(" [position independent]")); if (flags & EF_ARM_NEW_ABI) fprintf (file, _(" [new ABI]")); if (flags & EF_ARM_OLD_ABI) fprintf (file, _(" [old ABI]")); if (flags & EF_ARM_SOFT_FLOAT) fprintf (file, _(" [software FP]")); flags &= ~(EF_ARM_INTERWORK | EF_ARM_APCS_26 | EF_ARM_APCS_FLOAT | EF_ARM_PIC | EF_ARM_NEW_ABI | EF_ARM_OLD_ABI | EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT | EF_ARM_MAVERICK_FLOAT); break; case EF_ARM_EABI_VER1: fprintf (file, _(" [Version1 EABI]")); if (flags & EF_ARM_SYMSARESORTED) fprintf (file, _(" [sorted symbol table]")); else fprintf (file, _(" [unsorted symbol table]")); flags &= ~ EF_ARM_SYMSARESORTED; break; case EF_ARM_EABI_VER2: fprintf (file, _(" [Version2 EABI]")); if (flags & EF_ARM_SYMSARESORTED) fprintf (file, _(" [sorted symbol table]")); else fprintf (file, _(" [unsorted symbol table]")); if (flags & EF_ARM_DYNSYMSUSESEGIDX) fprintf (file, _(" [dynamic symbols use segment index]")); if (flags & EF_ARM_MAPSYMSFIRST) fprintf (file, _(" [mapping symbols precede others]")); flags &= ~(EF_ARM_SYMSARESORTED | EF_ARM_DYNSYMSUSESEGIDX | EF_ARM_MAPSYMSFIRST); break; case EF_ARM_EABI_VER3: fprintf (file, _(" [Version3 EABI]")); break; case EF_ARM_EABI_VER4: fprintf (file, _(" [Version4 EABI]")); goto eabi; case EF_ARM_EABI_VER5: fprintf (file, _(" [Version5 EABI]")); eabi: if (flags & EF_ARM_BE8) fprintf (file, _(" [BE8]")); if (flags & EF_ARM_LE8) fprintf (file, _(" [LE8]")); flags &= ~(EF_ARM_LE8 | EF_ARM_BE8); break; default: fprintf (file, _(" ")); break; } flags &= ~ EF_ARM_EABIMASK; if (flags & EF_ARM_RELEXEC) fprintf (file, _(" [relocatable executable]")); if (flags & EF_ARM_HASENTRY) fprintf (file, _(" [has entry point]")); flags &= ~ (EF_ARM_RELEXEC | EF_ARM_HASENTRY); if (flags) fprintf (file, _("")); fputc ('\n', file); return TRUE; } static int elf32_arm_get_symbol_type (Elf_Internal_Sym * elf_sym, int type) { switch (ELF_ST_TYPE (elf_sym->st_info)) { case STT_ARM_TFUNC: return ELF_ST_TYPE (elf_sym->st_info); case STT_ARM_16BIT: /* If the symbol is not an object, return the STT_ARM_16BIT flag. This allows us to distinguish between data used by Thumb instructions and non-data (which is probably code) inside Thumb regions of an executable. */ if (type != STT_OBJECT && type != STT_TLS) return ELF_ST_TYPE (elf_sym->st_info); break; default: break; } return type; } static asection * elf32_arm_gc_mark_hook (asection *sec, struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { if (h != NULL) switch (ELF32_R_TYPE (rel->r_info)) { case R_ARM_GNU_VTINHERIT: case R_ARM_GNU_VTENTRY: return NULL; } return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } /* Update the got entry reference counts for the section being removed. */ static bfd_boolean elf32_arm_gc_sweep_hook (bfd * abfd, struct bfd_link_info * info, asection * sec, const Elf_Internal_Rela * relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; bfd_signed_vma *local_got_refcounts; const Elf_Internal_Rela *rel, *relend; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); elf_section_data (sec)->local_dynrel = NULL; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); local_got_refcounts = elf_local_got_refcounts (abfd); relend = relocs + sec->reloc_count; for (rel = relocs; rel < relend; rel++) { unsigned long r_symndx; struct elf_link_hash_entry *h = NULL; int r_type; r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx >= symtab_hdr->sh_info) { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; } r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (globals, r_type); switch (r_type) { case R_ARM_GOT32: case R_ARM_GOT_PREL: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: if (h != NULL) { if (h->got.refcount > 0) h->got.refcount -= 1; } else if (local_got_refcounts != NULL) { if (local_got_refcounts[r_symndx] > 0) local_got_refcounts[r_symndx] -= 1; } break; case R_ARM_TLS_LDM32: elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1; break; case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PREL31: case R_ARM_THM_CALL: case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Should the interworking branches be here also? */ if (h != NULL) { struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied **pp; struct elf32_arm_relocs_copied *p; eh = (struct elf32_arm_link_hash_entry *) h; if (h->plt.refcount > 0) { h->plt.refcount -= 1; if (ELF32_R_TYPE (rel->r_info) == R_ARM_THM_CALL) eh->plt_thumb_refcount--; } if (r_type == R_ARM_ABS32 || r_type == R_ARM_REL32 || r_type == R_ARM_ABS32_NOI || r_type == R_ARM_REL32_NOI) { for (pp = &eh->relocs_copied; (p = *pp) != NULL; pp = &p->next) if (p->section == sec) { p->count -= 1; if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32 || ELF32_R_TYPE (rel->r_info) == R_ARM_REL32_NOI) p->pc_count -= 1; if (p->count == 0) *pp = p->next; break; } } } break; default: break; } } return TRUE; } /* Look through the relocs for a section during the first phase. */ static bfd_boolean elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; struct elf_link_hash_entry **sym_hashes_end; const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; bfd *dynobj; asection *sreloc; bfd_vma *local_got_offsets; struct elf32_arm_link_hash_table *htab; if (info->relocatable) return TRUE; htab = elf32_arm_hash_table (info); sreloc = NULL; /* Create dynamic sections for relocatable executables so that we can copy relocations. */ if (htab->root.is_relocatable_executable && ! htab->root.dynamic_sections_created) { if (! _bfd_elf_link_create_dynamic_sections (abfd, info)) return FALSE; } dynobj = elf_hash_table (info)->dynobj; local_got_offsets = elf_local_got_offsets (abfd); symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); sym_hashes_end = sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); if (!elf_bad_symtab (abfd)) sym_hashes_end -= symtab_hdr->sh_info; rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { struct elf_link_hash_entry *h; struct elf32_arm_link_hash_entry *eh; unsigned long r_symndx; int r_type; r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (htab, r_type); if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) { (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd, r_symndx); return FALSE; } if (r_symndx < symtab_hdr->sh_info) h = NULL; else { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; } eh = (struct elf32_arm_link_hash_entry *) h; switch (r_type) { case R_ARM_GOT32: case R_ARM_GOT_PREL: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: /* This symbol requires a global offset table entry. */ { int tls_type, old_tls_type; switch (r_type) { case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break; case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break; default: tls_type = GOT_NORMAL; break; } if (h != NULL) { h->got.refcount++; old_tls_type = elf32_arm_hash_entry (h)->tls_type; } else { bfd_signed_vma *local_got_refcounts; /* This is a global offset table entry for a local symbol. */ local_got_refcounts = elf_local_got_refcounts (abfd); if (local_got_refcounts == NULL) { bfd_size_type size; size = symtab_hdr->sh_info; size *= (sizeof (bfd_signed_vma) + sizeof(char)); local_got_refcounts = bfd_zalloc (abfd, size); if (local_got_refcounts == NULL) return FALSE; elf_local_got_refcounts (abfd) = local_got_refcounts; elf32_arm_local_got_tls_type (abfd) = (char *) (local_got_refcounts + symtab_hdr->sh_info); } local_got_refcounts[r_symndx] += 1; old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx]; } /* We will already have issued an error message if there is a TLS / non-TLS mismatch, based on the symbol type. We don't support any linker relaxations. So just combine any TLS types needed. */ if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL && tls_type != GOT_NORMAL) tls_type |= old_tls_type; if (old_tls_type != tls_type) { if (h != NULL) elf32_arm_hash_entry (h)->tls_type = tls_type; else elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type; } } /* Fall through */ case R_ARM_TLS_LDM32: if (r_type == R_ARM_TLS_LDM32) htab->tls_ldm_got.refcount++; /* Fall through */ case R_ARM_GOTOFF32: case R_ARM_GOTPC: if (htab->sgot == NULL) { if (htab->root.dynobj == NULL) htab->root.dynobj = abfd; if (!create_got_section (htab->root.dynobj, info)) return FALSE; } break; case R_ARM_ABS12: /* VxWorks uses dynamic R_ARM_ABS12 relocations for ldr __GOTT_INDEX__ offsets. */ if (!htab->vxworks_p) break; /* Fall through */ case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PREL31: case R_ARM_THM_CALL: case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Should the interworking branches be listed here? */ if (h != NULL) { /* If this reloc is in a read-only section, we might need a copy reloc. We can't check reliably at this stage whether the section is read-only, as input sections have not yet been mapped to output sections. Tentatively set the flag for now, and correct in adjust_dynamic_symbol. */ if (!info->shared) h->non_got_ref = 1; /* We may need a .plt entry if the function this reloc refers to is in a different object. We can't tell for sure yet, because something later might force the symbol local. */ - if (r_type != R_ARM_ABS32 - && r_type != R_ARM_REL32 - && r_type != R_ARM_ABS32_NOI - && r_type != R_ARM_REL32_NOI - && r_type != R_ARM_ABS12) - h->needs_plt = 1; + switch (r_type) + { + case R_ARM_ABS12: + case R_ARM_ABS32: + case R_ARM_ABS32_NOI: + case R_ARM_REL32: + case R_ARM_REL32_NOI: + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + case R_ARM_MOVW_PREL_NC: + case R_ARM_MOVT_PREL: + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVT_PREL: + break; + default: + h->needs_plt = 1; + break; + } /* If we create a PLT entry, this relocation will reference it, even if it's an ABS32 relocation. */ h->plt.refcount += 1; if (r_type == R_ARM_THM_CALL) eh->plt_thumb_refcount += 1; } /* If we are creating a shared library or relocatable executable, and this is a reloc against a global symbol, or a non PC relative reloc against a local symbol, then we need to copy the reloc into the shared library. However, if we are linking with -Bsymbolic, we do not need to copy a reloc against a global symbol which is defined in an object we are including in the link (i.e., DEF_REGULAR is set). At this point we have not seen all the input files, so it is possible that DEF_REGULAR is not set now but will be set later (it is never cleared). We account for that possibility below by storing information in the relocs_copied field of the hash table entry. */ if ((info->shared || htab->root.is_relocatable_executable) && (sec->flags & SEC_ALLOC) != 0 && ((r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI) || (h != NULL && ! h->needs_plt && (! info->symbolic || ! h->def_regular)))) { struct elf32_arm_relocs_copied *p, **head; /* When creating a shared object, we must copy these reloc types into the output file. We create a reloc section in dynobj and make room for this reloc. */ if (sreloc == NULL) { const char * name; name = (bfd_elf_string_from_elf_section (abfd, elf_elfheader (abfd)->e_shstrndx, elf_section_data (sec)->rel_hdr.sh_name)); if (name == NULL) return FALSE; BFD_ASSERT (reloc_section_p (htab, name, sec)); sreloc = bfd_get_section_by_name (dynobj, name); if (sreloc == NULL) { flagword flags; flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_IN_MEMORY | SEC_LINKER_CREATED); if ((sec->flags & SEC_ALLOC) != 0 /* BPABI objects never have dynamic relocations mapped. */ && !htab->symbian_p) flags |= SEC_ALLOC | SEC_LOAD; sreloc = bfd_make_section_with_flags (dynobj, name, flags); if (sreloc == NULL || ! bfd_set_section_alignment (dynobj, sreloc, 2)) return FALSE; } elf_section_data (sec)->sreloc = sreloc; } /* If this is a global symbol, we count the number of relocations we need for this symbol. */ if (h != NULL) { head = &((struct elf32_arm_link_hash_entry *) h)->relocs_copied; } else { /* Track dynamic relocs needed for local syms too. We really need local syms available to do this easily. Oh well. */ asection *s; void *vpp; s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, sec, r_symndx); if (s == NULL) return FALSE; vpp = &elf_section_data (s)->local_dynrel; head = (struct elf32_arm_relocs_copied **) vpp; } p = *head; if (p == NULL || p->section != sec) { bfd_size_type amt = sizeof *p; p = bfd_alloc (htab->root.dynobj, amt); if (p == NULL) return FALSE; p->next = *head; *head = p; p->section = sec; p->count = 0; p->pc_count = 0; } if (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI) p->pc_count += 1; p->count += 1; } break; /* This relocation describes the C++ object vtable hierarchy. Reconstruct it for later use during GC. */ case R_ARM_GNU_VTINHERIT: if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) return FALSE; break; /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_ARM_GNU_VTENTRY: if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset)) return FALSE; break; } } return TRUE; } /* Unwinding tables are not referenced directly. This pass marks them as required if the corresponding code section is marked. */ static bfd_boolean elf32_arm_gc_mark_extra_sections(struct bfd_link_info *info, elf_gc_mark_hook_fn gc_mark_hook) { bfd *sub; Elf_Internal_Shdr **elf_shdrp; bfd_boolean again; /* Marking EH data may cause additional code sections to be marked, requiring multiple passes. */ again = TRUE; while (again) { again = FALSE; for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) { asection *o; if (bfd_get_flavour (sub) != bfd_target_elf_flavour) continue; elf_shdrp = elf_elfsections (sub); for (o = sub->sections; o != NULL; o = o->next) { Elf_Internal_Shdr *hdr; hdr = &elf_section_data (o)->this_hdr; if (hdr->sh_type == SHT_ARM_EXIDX && hdr->sh_link && !o->gc_mark && elf_shdrp[hdr->sh_link]->bfd_section->gc_mark) { again = TRUE; if (!_bfd_elf_gc_mark (info, o, gc_mark_hook)) return FALSE; } } } } return TRUE; } /* Treat mapping symbols as special target symbols. */ static bfd_boolean elf32_arm_is_target_special_symbol (bfd * abfd ATTRIBUTE_UNUSED, asymbol * sym) { return bfd_is_arm_special_symbol_name (sym->name, BFD_ARM_SPECIAL_SYM_TYPE_ANY); } /* This is a copy of elf_find_function() from elf.c except that ARM mapping symbols are ignored when looking for function names and STT_ARM_TFUNC is considered to a function type. */ static bfd_boolean arm_elf_find_function (bfd * abfd ATTRIBUTE_UNUSED, asection * section, asymbol ** symbols, bfd_vma offset, const char ** filename_ptr, const char ** functionname_ptr) { const char * filename = NULL; asymbol * func = NULL; bfd_vma low_func = 0; asymbol ** p; for (p = symbols; *p != NULL; p++) { elf_symbol_type *q; q = (elf_symbol_type *) *p; switch (ELF_ST_TYPE (q->internal_elf_sym.st_info)) { default: break; case STT_FILE: filename = bfd_asymbol_name (&q->symbol); break; case STT_FUNC: case STT_ARM_TFUNC: case STT_NOTYPE: /* Skip mapping symbols. */ if ((q->symbol.flags & BSF_LOCAL) && bfd_is_arm_special_symbol_name (q->symbol.name, BFD_ARM_SPECIAL_SYM_TYPE_ANY)) continue; /* Fall through. */ if (bfd_get_section (&q->symbol) == section && q->symbol.value >= low_func && q->symbol.value <= offset) { func = (asymbol *) q; low_func = q->symbol.value; } break; } } if (func == NULL) return FALSE; if (filename_ptr) *filename_ptr = filename; if (functionname_ptr) *functionname_ptr = bfd_asymbol_name (func); return TRUE; } /* Find the nearest line to a particular section and offset, for error reporting. This code is a duplicate of the code in elf.c, except that it uses arm_elf_find_function. */ static bfd_boolean elf32_arm_find_nearest_line (bfd * abfd, asection * section, asymbol ** symbols, bfd_vma offset, const char ** filename_ptr, const char ** functionname_ptr, unsigned int * line_ptr) { bfd_boolean found = FALSE; /* We skip _bfd_dwarf1_find_nearest_line since no known ARM toolchain uses it. */ if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset, filename_ptr, functionname_ptr, line_ptr, 0, & elf_tdata (abfd)->dwarf2_find_line_info)) { if (!*functionname_ptr) arm_elf_find_function (abfd, section, symbols, offset, *filename_ptr ? NULL : filename_ptr, functionname_ptr); return TRUE; } if (! _bfd_stab_section_find_nearest_line (abfd, symbols, section, offset, & found, filename_ptr, functionname_ptr, line_ptr, & elf_tdata (abfd)->line_info)) return FALSE; if (found && (*functionname_ptr || *line_ptr)) return TRUE; if (symbols == NULL) return FALSE; if (! arm_elf_find_function (abfd, section, symbols, offset, filename_ptr, functionname_ptr)) return FALSE; *line_ptr = 0; return TRUE; } static bfd_boolean elf32_arm_find_inliner_info (bfd * abfd, const char ** filename_ptr, const char ** functionname_ptr, unsigned int * line_ptr) { bfd_boolean found; found = _bfd_dwarf2_find_inliner_info (abfd, filename_ptr, functionname_ptr, line_ptr, & elf_tdata (abfd)->dwarf2_find_line_info); return found; } /* Adjust a symbol defined by a dynamic object and referenced by a regular object. The current definition is in some section of the dynamic object, but we're not including those sections. We have to change the definition to something the rest of the link can understand. */ static bfd_boolean elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info, struct elf_link_hash_entry * h) { bfd * dynobj; asection * s; struct elf32_arm_link_hash_entry * eh; struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (info); dynobj = elf_hash_table (info)->dynobj; /* Make sure we know what is going on here. */ BFD_ASSERT (dynobj != NULL && (h->needs_plt || h->u.weakdef != NULL || (h->def_dynamic && h->ref_regular && !h->def_regular))); eh = (struct elf32_arm_link_hash_entry *) h; /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later, when we know the address of the .got section. */ if (h->type == STT_FUNC || h->type == STT_ARM_TFUNC || h->needs_plt) { if (h->plt.refcount <= 0 || SYMBOL_CALLS_LOCAL (info, h) || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT && h->root.type == bfd_link_hash_undefweak)) { /* This case can occur if we saw a PLT32 reloc in an input file, but the symbol was never referred to by a dynamic object, or if all references were garbage collected. In such a case, we don't actually need to build a procedure linkage table, and we can just do a PC24 reloc instead. */ h->plt.offset = (bfd_vma) -1; eh->plt_thumb_refcount = 0; h->needs_plt = 0; } return TRUE; } else { /* It's possible that we incorrectly decided a .plt reloc was needed for an R_ARM_PC24 or similar reloc to a non-function sym in check_relocs. We can't decide accurately between function and non-function syms in check-relocs; Objects loaded later in the link may change h->type. So fix it now. */ h->plt.offset = (bfd_vma) -1; eh->plt_thumb_refcount = 0; } /* If this is a weak symbol, and there is a real definition, the processor independent code will have arranged for us to see the real definition first, and we can just use the same value. */ if (h->u.weakdef != NULL) { BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined || h->u.weakdef->root.type == bfd_link_hash_defweak); h->root.u.def.section = h->u.weakdef->root.u.def.section; h->root.u.def.value = h->u.weakdef->root.u.def.value; return TRUE; } /* If there are no non-GOT references, we do not need a copy relocation. */ if (!h->non_got_ref) return TRUE; /* This is a reference to a symbol defined by a dynamic object which is not a function. */ /* If we are creating a shared library, we must presume that the only references to the symbol are via the global offset table. For such cases we need not do anything here; the relocations will be handled correctly by relocate_section. Relocatable executables can reference data in shared objects directly, so we don't need to do anything here. */ if (info->shared || globals->root.is_relocatable_executable) return TRUE; if (h->size == 0) { (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), h->root.root.string); return TRUE; } /* We must allocate the symbol in our .dynbss section, which will become part of the .bss section of the executable. There will be an entry for this symbol in the .dynsym section. The dynamic object will contain position independent code, so all references from the dynamic object to this symbol will go through the global offset table. The dynamic linker will use the .dynsym entry to determine the address it must put in the global offset table, so both the dynamic object and the regular object will refer to the same memory location for the variable. */ s = bfd_get_section_by_name (dynobj, ".dynbss"); BFD_ASSERT (s != NULL); /* We must generate a R_ARM_COPY reloc to tell the dynamic linker to copy the initial value out of the dynamic object and into the runtime process image. We need to remember the offset into the .rel(a).bss section we are going to use. */ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) { asection *srel; srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (globals, ".bss")); BFD_ASSERT (srel != NULL); srel->size += RELOC_SIZE (globals); h->needs_copy = 1; } return _bfd_elf_adjust_dynamic_copy (h, s); } /* Allocate space in .plt, .got and associated reloc sections for dynamic relocs. */ static bfd_boolean allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) { struct bfd_link_info *info; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied *p; eh = (struct elf32_arm_link_hash_entry *) h; if (h->root.type == bfd_link_hash_indirect) return TRUE; if (h->root.type == bfd_link_hash_warning) /* When warning symbols are created, they **replace** the "real" entry in the hash table, thus we never get to see the real symbol in a hash traversal. So look at it now. */ h = (struct elf_link_hash_entry *) h->root.u.i.link; info = (struct bfd_link_info *) inf; htab = elf32_arm_hash_table (info); if (htab->root.dynamic_sections_created && h->plt.refcount > 0) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } if (info->shared || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h)) { asection *s = htab->splt; /* If this is the first .plt entry, make room for the special first entry. */ if (s->size == 0) s->size += htab->plt_header_size; h->plt.offset = s->size; /* If we will insert a Thumb trampoline before this PLT, leave room for it. */ if (!htab->use_blx && eh->plt_thumb_refcount > 0) { h->plt.offset += PLT_THUMB_STUB_SIZE; s->size += PLT_THUMB_STUB_SIZE; } /* If this symbol is not defined in a regular file, and we are not generating a shared library, then set the symbol to this location in the .plt. This is required to make function pointers compare as equal between the normal executable and the shared library. */ if (! info->shared && !h->def_regular) { h->root.u.def.section = s; h->root.u.def.value = h->plt.offset; /* Make sure the function is not marked as Thumb, in case it is the target of an ABS32 relocation, which will point to the PLT entry. */ if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC) h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC); } /* Make room for this entry. */ s->size += htab->plt_entry_size; if (!htab->symbian_p) { /* We also need to make an entry in the .got.plt section, which will be placed in the .got section by the linker script. */ eh->plt_got_offset = htab->sgotplt->size; htab->sgotplt->size += 4; } /* We also need to make an entry in the .rel(a).plt section. */ htab->srelplt->size += RELOC_SIZE (htab); /* VxWorks executables have a second set of relocations for each PLT entry. They go in a separate relocation section, which is processed by the kernel loader. */ if (htab->vxworks_p && !info->shared) { /* There is a relocation for the initial PLT entry: an R_ARM_32 relocation for _GLOBAL_OFFSET_TABLE_. */ if (h->plt.offset == htab->plt_header_size) htab->srelplt2->size += RELOC_SIZE (htab); /* There are two extra relocations for each subsequent PLT entry: an R_ARM_32 relocation for the GOT entry, and an R_ARM_32 relocation for the PLT entry. */ htab->srelplt2->size += RELOC_SIZE (htab) * 2; } } else { h->plt.offset = (bfd_vma) -1; h->needs_plt = 0; } } else { h->plt.offset = (bfd_vma) -1; h->needs_plt = 0; } if (h->got.refcount > 0) { asection *s; bfd_boolean dyn; int tls_type = elf32_arm_hash_entry (h)->tls_type; int indx; /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } if (!htab->symbian_p) { s = htab->sgot; h->got.offset = s->size; if (tls_type == GOT_UNKNOWN) abort (); if (tls_type == GOT_NORMAL) /* Non-TLS symbols need one GOT slot. */ s->size += 4; else { if (tls_type & GOT_TLS_GD) /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. */ s->size += 8; if (tls_type & GOT_TLS_IE) /* R_ARM_TLS_IE32 needs one GOT slot. */ s->size += 4; } dyn = htab->root.dynamic_sections_created; indx = 0; if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h))) indx = h->dynindx; if (tls_type != GOT_NORMAL && (info->shared || indx != 0) && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) { if (tls_type & GOT_TLS_IE) htab->srelgot->size += RELOC_SIZE (htab); if (tls_type & GOT_TLS_GD) htab->srelgot->size += RELOC_SIZE (htab); if ((tls_type & GOT_TLS_GD) && indx != 0) htab->srelgot->size += RELOC_SIZE (htab); } else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak) && (info->shared || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) htab->srelgot->size += RELOC_SIZE (htab); } } else h->got.offset = (bfd_vma) -1; /* Allocate stubs for exported Thumb functions on v4t. */ if (!htab->use_blx && h->dynindx != -1 && h->def_regular && ELF_ST_TYPE (h->type) == STT_ARM_TFUNC && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) { struct elf_link_hash_entry * th; struct bfd_link_hash_entry * bh; struct elf_link_hash_entry * myh; char name[1024]; asection *s; bh = NULL; /* Create a new symbol to regist the real location of the function. */ s = h->root.u.def.section; sprintf(name, "__real_%s", h->root.root.string); _bfd_generic_link_add_one_symbol (info, s->owner, name, BSF_GLOBAL, s, h->root.u.def.value, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC); myh->forced_local = 1; eh->export_glue = myh; th = record_arm_to_thumb_glue (info, h); /* Point the symbol at the stub. */ h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC); h->root.u.def.section = th->root.u.def.section; h->root.u.def.value = th->root.u.def.value & ~1; } if (eh->relocs_copied == NULL) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for dynamic pc-relative relocs against symbols which turn out to be defined in regular objects. For the normal shared case, discard space for pc-relative relocs that have become local due to symbol visibility changes. */ if (info->shared || htab->root.is_relocatable_executable) { /* The only relocs that use pc_count are R_ARM_REL32 and R_ARM_REL32_NOI, which will appear on something like ".long foo - .". We want calls to protected symbols to resolve directly to the function rather than going via the plt. If people want function pointer comparisons to work as expected then they should avoid writing assembly like ".long foo - .". */ if (SYMBOL_CALLS_LOCAL (info, h)) { struct elf32_arm_relocs_copied **pp; for (pp = &eh->relocs_copied; (p = *pp) != NULL; ) { p->count -= p->pc_count; p->pc_count = 0; if (p->count == 0) *pp = p->next; else pp = &p->next; } } /* Also discard relocs on undefined weak syms with non-default visibility. */ if (eh->relocs_copied != NULL && h->root.type == bfd_link_hash_undefweak) { if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) eh->relocs_copied = NULL; /* Make sure undefined weak symbols are output as a dynamic symbol in PIEs. */ else if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } } else if (htab->root.is_relocatable_executable && h->dynindx == -1 && h->root.type == bfd_link_hash_new) { /* Output absolute symbols so that we can create relocations against them. For normal symbols we output a relocation against the section that contains them. */ if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } } else { /* For the non-shared case, discard space for relocs against symbols which turn out to need copy relocs or are not dynamic. */ if (!h->non_got_ref && ((h->def_dynamic && !h->def_regular) || (htab->root.dynamic_sections_created && (h->root.type == bfd_link_hash_undefweak || h->root.type == bfd_link_hash_undefined)))) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } /* If that succeeded, we know we'll be keeping all the relocs. */ if (h->dynindx != -1) goto keep; } eh->relocs_copied = NULL; keep: ; } /* Finally, allocate space. */ for (p = eh->relocs_copied; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->section)->sreloc; sreloc->size += p->count * RELOC_SIZE (htab); } return TRUE; } /* Find any dynamic relocs that apply to read-only sections. */ static bfd_boolean elf32_arm_readonly_dynrelocs (struct elf_link_hash_entry *h, PTR inf) { struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied *p; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; eh = (struct elf32_arm_link_hash_entry *) h; for (p = eh->relocs_copied; p != NULL; p = p->next) { asection *s = p->section; if (s != NULL && (s->flags & SEC_READONLY) != 0) { struct bfd_link_info *info = (struct bfd_link_info *) inf; info->flags |= DF_TEXTREL; /* Not an error, just cut short the traversal. */ return FALSE; } } return TRUE; } void bfd_elf32_arm_set_byteswap_code (struct bfd_link_info *info, int byteswap_code) { struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (info); globals->byteswap_code = byteswap_code; } /* Set the sizes of the dynamic sections. */ static bfd_boolean elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED, struct bfd_link_info * info) { bfd * dynobj; asection * s; bfd_boolean plt; bfd_boolean relocs; bfd *ibfd; struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); check_use_blx (htab); if (elf_hash_table (info)->dynamic_sections_created) { /* Set the contents of the .interp section to the interpreter. */ if (info->executable) { s = bfd_get_section_by_name (dynobj, ".interp"); BFD_ASSERT (s != NULL); s->size = sizeof ELF_DYNAMIC_INTERPRETER; s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; } } /* Set up .got offsets for local syms, and space for local dynamic relocs. */ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { bfd_signed_vma *local_got; bfd_signed_vma *end_local_got; char *local_tls_type; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) continue; for (s = ibfd->sections; s != NULL; s = s->next) { struct elf32_arm_relocs_copied *p; for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) { if (!bfd_is_abs_section (p->section) && bfd_is_abs_section (p->section->output_section)) { /* Input section has been discarded, either because it is a copy of a linkonce section or due to linker script /DISCARD/, so we'll be discarding the relocs too. */ } else if (p->count != 0) { srel = elf_section_data (p->section)->sreloc; srel->size += p->count * RELOC_SIZE (htab); if ((p->section->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; } } } local_got = elf_local_got_refcounts (ibfd); if (!local_got) continue; symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; local_tls_type = elf32_arm_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; for (; local_got < end_local_got; ++local_got, ++local_tls_type) { if (*local_got > 0) { *local_got = s->size; if (*local_tls_type & GOT_TLS_GD) /* TLS_GD relocs need an 8-byte structure in the GOT. */ s->size += 8; if (*local_tls_type & GOT_TLS_IE) s->size += 4; if (*local_tls_type == GOT_NORMAL) s->size += 4; if (info->shared || *local_tls_type == GOT_TLS_GD) srel->size += RELOC_SIZE (htab); } else *local_got = (bfd_vma) -1; } } if (htab->tls_ldm_got.refcount > 0) { /* Allocate two GOT entries and one dynamic relocation (if necessary) for R_ARM_TLS_LDM32 relocations. */ htab->tls_ldm_got.offset = htab->sgot->size; htab->sgot->size += 8; if (info->shared) htab->srelgot->size += RELOC_SIZE (htab); } else htab->tls_ldm_got.offset = -1; /* Allocate global sym .plt and .got entries, and space for global sym dynamic relocs. */ elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); /* Here we rummage through the found bfds to collect glue information. */ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { /* Initialise mapping tables for code/data. */ bfd_elf32_arm_init_maps (ibfd); if (!bfd_elf32_arm_process_before_allocation (ibfd, info) || !bfd_elf32_arm_vfp11_erratum_scan (ibfd, info)) /* xgettext:c-format */ _bfd_error_handler (_("Errors encountered processing file %s"), ibfd->filename); } /* The check_relocs and adjust_dynamic_symbol entry points have determined the sizes of the various dynamic sections. Allocate memory for them. */ plt = FALSE; relocs = FALSE; for (s = dynobj->sections; s != NULL; s = s->next) { const char * name; if ((s->flags & SEC_LINKER_CREATED) == 0) continue; /* It's OK to base decisions on the section name, because none of the dynobj section names depend upon the input files. */ name = bfd_get_section_name (dynobj, s); if (strcmp (name, ".plt") == 0) { /* Remember whether there is a PLT. */ plt = s->size != 0; } else if (CONST_STRNEQ (name, ".rel")) { if (s->size != 0) { /* Remember whether there are any reloc sections other than .rel(a).plt and .rela.plt.unloaded. */ if (s != htab->srelplt && s != htab->srelplt2) relocs = TRUE; /* We use the reloc_count field as a counter if we need to copy relocs into the output file. */ s->reloc_count = 0; } } else if (! CONST_STRNEQ (name, ".got") && strcmp (name, ".dynbss") != 0) { /* It's not one of our sections, so don't allocate space. */ continue; } if (s->size == 0) { /* If we don't need this section, strip it from the output file. This is mostly to handle .rel(a).bss and .rel(a).plt. We must create both sections in create_dynamic_sections, because they must be created before the linker maps input sections to output sections. The linker does that before adjust_dynamic_symbol is called, and it is that function which decides whether anything needs to go into these sections. */ s->flags |= SEC_EXCLUDE; continue; } if ((s->flags & SEC_HAS_CONTENTS) == 0) continue; /* Allocate memory for the section contents. */ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); if (s->contents == NULL) return FALSE; } if (elf_hash_table (info)->dynamic_sections_created) { /* Add some entries to the .dynamic section. We fill in the values later, in elf32_arm_finish_dynamic_sections, but we must add the entries now so that we get the correct size for the .dynamic section. The DT_DEBUG entry is filled in by the dynamic linker and used by the debugger. */ #define add_dynamic_entry(TAG, VAL) \ _bfd_elf_add_dynamic_entry (info, TAG, VAL) if (info->executable) { if (!add_dynamic_entry (DT_DEBUG, 0)) return FALSE; } if (plt) { if ( !add_dynamic_entry (DT_PLTGOT, 0) || !add_dynamic_entry (DT_PLTRELSZ, 0) || !add_dynamic_entry (DT_PLTREL, htab->use_rel ? DT_REL : DT_RELA) || !add_dynamic_entry (DT_JMPREL, 0)) return FALSE; } if (relocs) { if (htab->use_rel) { if (!add_dynamic_entry (DT_REL, 0) || !add_dynamic_entry (DT_RELSZ, 0) || !add_dynamic_entry (DT_RELENT, RELOC_SIZE (htab))) return FALSE; } else { if (!add_dynamic_entry (DT_RELA, 0) || !add_dynamic_entry (DT_RELASZ, 0) || !add_dynamic_entry (DT_RELAENT, RELOC_SIZE (htab))) return FALSE; } } /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) elf_link_hash_traverse (&htab->root, elf32_arm_readonly_dynrelocs, (PTR) info); if ((info->flags & DF_TEXTREL) != 0) { if (!add_dynamic_entry (DT_TEXTREL, 0)) return FALSE; } } #undef add_dynamic_entry return TRUE; } /* Finish up dynamic symbol handling. We set the contents of various dynamic sections here. */ static bfd_boolean elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info, struct elf_link_hash_entry * h, Elf_Internal_Sym * sym) { bfd * dynobj; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; dynobj = elf_hash_table (info)->dynobj; htab = elf32_arm_hash_table (info); eh = (struct elf32_arm_link_hash_entry *) h; if (h->plt.offset != (bfd_vma) -1) { asection * splt; asection * srel; bfd_byte *loc; bfd_vma plt_index; Elf_Internal_Rela rel; /* This symbol has an entry in the procedure linkage table. Set it up. */ BFD_ASSERT (h->dynindx != -1); splt = bfd_get_section_by_name (dynobj, ".plt"); srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt")); BFD_ASSERT (splt != NULL && srel != NULL); /* Fill in the entry in the procedure linkage table. */ if (htab->symbian_p) { put_arm_insn (htab, output_bfd, elf32_arm_symbian_plt_entry[0], splt->contents + h->plt.offset); bfd_put_32 (output_bfd, elf32_arm_symbian_plt_entry[1], splt->contents + h->plt.offset + 4); /* Fill in the entry in the .rel.plt section. */ rel.r_offset = (splt->output_section->vma + splt->output_offset + h->plt.offset + 4); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT); /* Get the index in the procedure linkage table which corresponds to this symbol. This is the index of this symbol in all the symbols for which we are making plt entries. The first entry in the procedure linkage table is reserved. */ plt_index = ((h->plt.offset - htab->plt_header_size) / htab->plt_entry_size); } else { bfd_vma got_offset, got_address, plt_address; bfd_vma got_displacement; asection * sgot; bfd_byte * ptr; sgot = bfd_get_section_by_name (dynobj, ".got.plt"); BFD_ASSERT (sgot != NULL); /* Get the offset into the .got.plt table of the entry that corresponds to this function. */ got_offset = eh->plt_got_offset; /* Get the index in the procedure linkage table which corresponds to this symbol. This is the index of this symbol in all the symbols for which we are making plt entries. The first three entries in .got.plt are reserved; after that symbols appear in the same order as in .plt. */ plt_index = (got_offset - 12) / 4; /* Calculate the address of the GOT entry. */ got_address = (sgot->output_section->vma + sgot->output_offset + got_offset); /* ...and the address of the PLT entry. */ plt_address = (splt->output_section->vma + splt->output_offset + h->plt.offset); ptr = htab->splt->contents + h->plt.offset; if (htab->vxworks_p && info->shared) { unsigned int i; bfd_vma val; for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4) { val = elf32_arm_vxworks_shared_plt_entry[i]; if (i == 2) val |= got_address - sgot->output_section->vma; if (i == 5) val |= plt_index * RELOC_SIZE (htab); if (i == 2 || i == 5) bfd_put_32 (output_bfd, val, ptr); else put_arm_insn (htab, output_bfd, val, ptr); } } else if (htab->vxworks_p) { unsigned int i; bfd_vma val; for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4) { val = elf32_arm_vxworks_exec_plt_entry[i]; if (i == 2) val |= got_address; if (i == 4) val |= 0xffffff & -((h->plt.offset + i * 4 + 8) >> 2); if (i == 5) val |= plt_index * RELOC_SIZE (htab); if (i == 2 || i == 5) bfd_put_32 (output_bfd, val, ptr); else put_arm_insn (htab, output_bfd, val, ptr); } loc = (htab->srelplt2->contents + (plt_index * 2 + 1) * RELOC_SIZE (htab)); /* Create the .rela.plt.unloaded R_ARM_ABS32 relocation referencing the GOT for this PLT entry. */ rel.r_offset = plt_address + 8; rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); rel.r_addend = got_offset; SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); loc += RELOC_SIZE (htab); /* Create the R_ARM_ABS32 relocation referencing the beginning of the PLT for this GOT entry. */ rel.r_offset = got_address; rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32); rel.r_addend = 0; SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } else { /* Calculate the displacement between the PLT slot and the entry in the GOT. The eight-byte offset accounts for the value produced by adding to pc in the first instruction of the PLT stub. */ got_displacement = got_address - (plt_address + 8); BFD_ASSERT ((got_displacement & 0xf0000000) == 0); if (!htab->use_blx && eh->plt_thumb_refcount > 0) { put_thumb_insn (htab, output_bfd, elf32_arm_plt_thumb_stub[0], ptr - 4); put_thumb_insn (htab, output_bfd, elf32_arm_plt_thumb_stub[1], ptr - 2); } put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[0] | ((got_displacement & 0x0ff00000) >> 20), ptr + 0); put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[1] | ((got_displacement & 0x000ff000) >> 12), ptr+ 4); put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[2] | (got_displacement & 0x00000fff), ptr + 8); #ifdef FOUR_WORD_PLT bfd_put_32 (output_bfd, elf32_arm_plt_entry[3], ptr + 12); #endif } /* Fill in the entry in the global offset table. */ bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset), sgot->contents + got_offset); /* Fill in the entry in the .rel(a).plt section. */ rel.r_addend = 0; rel.r_offset = got_address; rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_JUMP_SLOT); } loc = srel->contents + plt_index * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); if (!h->def_regular) { /* Mark the symbol as undefined, rather than as defined in the .plt section. Leave the value alone. */ sym->st_shndx = SHN_UNDEF; /* If the symbol is weak, we do need to clear the value. Otherwise, the PLT entry would provide a definition for the symbol even if the symbol wasn't defined anywhere, and so the symbol would never be NULL. */ if (!h->ref_regular_nonweak) sym->st_value = 0; } } if (h->got.offset != (bfd_vma) -1 && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) { asection * sgot; asection * srel; Elf_Internal_Rela rel; bfd_byte *loc; bfd_vma offset; /* This symbol has an entry in the global offset table. Set it up. */ sgot = bfd_get_section_by_name (dynobj, ".got"); srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".got")); BFD_ASSERT (sgot != NULL && srel != NULL); offset = (h->got.offset & ~(bfd_vma) 1); rel.r_addend = 0; rel.r_offset = (sgot->output_section->vma + sgot->output_offset + offset); /* If this is a static link, or it is a -Bsymbolic link and the symbol is defined locally or was forced to be local because of a version file, we just want to emit a RELATIVE reloc. The entry in the global offset table will already have been initialized in the relocate_section function. */ if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) { BFD_ASSERT((h->got.offset & 1) != 0); rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE); if (!htab->use_rel) { rel.r_addend = bfd_get_32 (output_bfd, sgot->contents + offset); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); } } else { BFD_ASSERT((h->got.offset & 1) == 0); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT); } loc = srel->contents + srel->reloc_count++ * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } if (h->needs_copy) { asection * s; Elf_Internal_Rela rel; bfd_byte *loc; /* This symbol needs a copy reloc. Set it up. */ BFD_ASSERT (h->dynindx != -1 && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)); s = bfd_get_section_by_name (h->root.u.def.section->owner, RELOC_SECTION (htab, ".bss")); BFD_ASSERT (s != NULL); rel.r_addend = 0; rel.r_offset = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_COPY); loc = s->contents + s->reloc_count++ * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. On VxWorks, the _GLOBAL_OFFSET_TABLE_ symbol is not absolute: it is relative to the ".got" section. */ if (strcmp (h->root.root.string, "_DYNAMIC") == 0 || (!htab->vxworks_p && h == htab->root.hgot)) sym->st_shndx = SHN_ABS; return TRUE; } /* Finish up the dynamic sections. */ static bfd_boolean elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info) { bfd * dynobj; asection * sgot; asection * sdyn; dynobj = elf_hash_table (info)->dynobj; sgot = bfd_get_section_by_name (dynobj, ".got.plt"); BFD_ASSERT (elf32_arm_hash_table (info)->symbian_p || sgot != NULL); sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); if (elf_hash_table (info)->dynamic_sections_created) { asection *splt; Elf32_External_Dyn *dyncon, *dynconend; struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); splt = bfd_get_section_by_name (dynobj, ".plt"); BFD_ASSERT (splt != NULL && sdyn != NULL); dyncon = (Elf32_External_Dyn *) sdyn->contents; dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); for (; dyncon < dynconend; dyncon++) { Elf_Internal_Dyn dyn; const char * name; asection * s; bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); switch (dyn.d_tag) { unsigned int type; default: break; case DT_HASH: name = ".hash"; goto get_vma_if_bpabi; case DT_STRTAB: name = ".dynstr"; goto get_vma_if_bpabi; case DT_SYMTAB: name = ".dynsym"; goto get_vma_if_bpabi; case DT_VERSYM: name = ".gnu.version"; goto get_vma_if_bpabi; case DT_VERDEF: name = ".gnu.version_d"; goto get_vma_if_bpabi; case DT_VERNEED: name = ".gnu.version_r"; goto get_vma_if_bpabi; case DT_PLTGOT: name = ".got"; goto get_vma; case DT_JMPREL: name = RELOC_SECTION (htab, ".plt"); get_vma: s = bfd_get_section_by_name (output_bfd, name); BFD_ASSERT (s != NULL); if (!htab->symbian_p) dyn.d_un.d_ptr = s->vma; else /* In the BPABI, tags in the PT_DYNAMIC section point at the file offset, not the memory address, for the convenience of the post linker. */ dyn.d_un.d_ptr = s->filepos; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; get_vma_if_bpabi: if (htab->symbian_p) goto get_vma; break; case DT_PLTRELSZ: s = bfd_get_section_by_name (output_bfd, RELOC_SECTION (htab, ".plt")); BFD_ASSERT (s != NULL); dyn.d_un.d_val = s->size; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; case DT_RELSZ: case DT_RELASZ: if (!htab->symbian_p) { /* My reading of the SVR4 ABI indicates that the procedure linkage table relocs (DT_JMPREL) should be included in the overall relocs (DT_REL). This is what Solaris does. However, UnixWare can not handle that case. Therefore, we override the DT_RELSZ entry here to make it not include the JMPREL relocs. Since the linker script arranges for .rel(a).plt to follow all other relocation sections, we don't have to worry about changing the DT_REL entry. */ s = bfd_get_section_by_name (output_bfd, RELOC_SECTION (htab, ".plt")); if (s != NULL) dyn.d_un.d_val -= s->size; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; } /* Fall through */ case DT_REL: case DT_RELA: /* In the BPABI, the DT_REL tag must point at the file offset, not the VMA, of the first relocation section. So, we use code similar to that in elflink.c, but do not check for SHF_ALLOC on the relcoation section, since relocations sections are never allocated under the BPABI. The comments above about Unixware notwithstanding, we include all of the relocations here. */ if (htab->symbian_p) { unsigned int i; type = ((dyn.d_tag == DT_REL || dyn.d_tag == DT_RELSZ) ? SHT_REL : SHT_RELA); dyn.d_un.d_val = 0; for (i = 1; i < elf_numsections (output_bfd); i++) { Elf_Internal_Shdr *hdr = elf_elfsections (output_bfd)[i]; if (hdr->sh_type == type) { if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) dyn.d_un.d_val += hdr->sh_size; else if ((ufile_ptr) hdr->sh_offset <= dyn.d_un.d_val - 1) dyn.d_un.d_val = hdr->sh_offset; } } bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); } break; /* Set the bottom bit of DT_INIT/FINI if the corresponding function is Thumb. */ case DT_INIT: name = info->init_function; goto get_sym; case DT_FINI: name = info->fini_function; get_sym: /* If it wasn't set by elf_bfd_final_link then there is nothing to adjust. */ if (dyn.d_un.d_val != 0) { struct elf_link_hash_entry * eh; eh = elf_link_hash_lookup (elf_hash_table (info), name, FALSE, FALSE, TRUE); if (eh != (struct elf_link_hash_entry *) NULL && ELF_ST_TYPE (eh->type) == STT_ARM_TFUNC) { dyn.d_un.d_val |= 1; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); } } break; } } /* Fill in the first entry in the procedure linkage table. */ if (splt->size > 0 && elf32_arm_hash_table (info)->plt_header_size) { const bfd_vma *plt0_entry; bfd_vma got_address, plt_address, got_displacement; /* Calculate the addresses of the GOT and PLT. */ got_address = sgot->output_section->vma + sgot->output_offset; plt_address = splt->output_section->vma + splt->output_offset; if (htab->vxworks_p) { /* The VxWorks GOT is relocated by the dynamic linker. Therefore, we must emit relocations rather than simply computing the values now. */ Elf_Internal_Rela rel; plt0_entry = elf32_arm_vxworks_exec_plt0_entry; put_arm_insn (htab, output_bfd, plt0_entry[0], splt->contents + 0); put_arm_insn (htab, output_bfd, plt0_entry[1], splt->contents + 4); put_arm_insn (htab, output_bfd, plt0_entry[2], splt->contents + 8); bfd_put_32 (output_bfd, got_address, splt->contents + 12); /* Generate a relocation for _GLOBAL_OFFSET_TABLE_. */ rel.r_offset = plt_address + 12; rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); rel.r_addend = 0; SWAP_RELOC_OUT (htab) (output_bfd, &rel, htab->srelplt2->contents); } else { got_displacement = got_address - (plt_address + 16); plt0_entry = elf32_arm_plt0_entry; put_arm_insn (htab, output_bfd, plt0_entry[0], splt->contents + 0); put_arm_insn (htab, output_bfd, plt0_entry[1], splt->contents + 4); put_arm_insn (htab, output_bfd, plt0_entry[2], splt->contents + 8); put_arm_insn (htab, output_bfd, plt0_entry[3], splt->contents + 12); #ifdef FOUR_WORD_PLT /* The displacement value goes in the otherwise-unused last word of the second entry. */ bfd_put_32 (output_bfd, got_displacement, splt->contents + 28); #else bfd_put_32 (output_bfd, got_displacement, splt->contents + 16); #endif } } /* UnixWare sets the entsize of .plt to 4, although that doesn't really seem like the right value. */ if (splt->output_section->owner == output_bfd) elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4; if (htab->vxworks_p && !info->shared && htab->splt->size > 0) { /* Correct the .rel(a).plt.unloaded relocations. They will have incorrect symbol indexes. */ int num_plts; unsigned char *p; num_plts = ((htab->splt->size - htab->plt_header_size) / htab->plt_entry_size); p = htab->srelplt2->contents + RELOC_SIZE (htab); for (; num_plts; num_plts--) { Elf_Internal_Rela rel; SWAP_RELOC_IN (htab) (output_bfd, p, &rel); rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); SWAP_RELOC_OUT (htab) (output_bfd, &rel, p); p += RELOC_SIZE (htab); SWAP_RELOC_IN (htab) (output_bfd, p, &rel); rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32); SWAP_RELOC_OUT (htab) (output_bfd, &rel, p); p += RELOC_SIZE (htab); } } } /* Fill in the first three entries in the global offset table. */ if (sgot) { if (sgot->size > 0) { if (sdyn == NULL) bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); else bfd_put_32 (output_bfd, sdyn->output_section->vma + sdyn->output_offset, sgot->contents); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8); } elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; } return TRUE; } static void elf32_arm_post_process_headers (bfd * abfd, struct bfd_link_info * link_info ATTRIBUTE_UNUSED) { Elf_Internal_Ehdr * i_ehdrp; /* ELF file header, internal form. */ struct elf32_arm_link_hash_table *globals; i_ehdrp = elf_elfheader (abfd); if (EF_ARM_EABI_VERSION (i_ehdrp->e_flags) == EF_ARM_EABI_UNKNOWN) i_ehdrp->e_ident[EI_OSABI] = ARM_ELF_OS_ABI_VERSION; else i_ehdrp->e_ident[EI_OSABI] = 0; i_ehdrp->e_ident[EI_ABIVERSION] = ARM_ELF_ABI_VERSION; if (link_info) { globals = elf32_arm_hash_table (link_info); if (globals->byteswap_code) i_ehdrp->e_flags |= EF_ARM_BE8; } /* * For EABI 5, we have to tag dynamic binaries and execs as either * soft float or hard float. */ if (EF_ARM_EABI_VERSION (i_ehdrp->e_flags) == EF_ARM_EABI_VER5 && (i_ehdrp->e_type == ET_DYN || i_ehdrp->e_type == ET_EXEC)) i_ehdrp->e_flags |= bfd_elf_get_obj_attr_int (abfd, OBJ_ATTR_PROC, Tag_ABI_VFP_args) ? EF_ARM_VFP_FLOAT : EF_ARM_SOFT_FLOAT; } static enum elf_reloc_type_class elf32_arm_reloc_type_class (const Elf_Internal_Rela *rela) { switch ((int) ELF32_R_TYPE (rela->r_info)) { case R_ARM_RELATIVE: return reloc_class_relative; case R_ARM_JUMP_SLOT: return reloc_class_plt; case R_ARM_COPY: return reloc_class_copy; default: return reloc_class_normal; } } /* Set the right machine number for an Arm ELF file. */ static bfd_boolean elf32_arm_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr) { if (hdr->sh_type == SHT_NOTE) *flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_CONTENTS; return TRUE; } static void elf32_arm_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED) { bfd_arm_update_notes (abfd, ARM_NOTE_SECTION); } /* Return TRUE if this is an unwinding table entry. */ static bfd_boolean is_arm_elf_unwind_section_name (bfd * abfd ATTRIBUTE_UNUSED, const char * name) { return (CONST_STRNEQ (name, ELF_STRING_ARM_unwind) || CONST_STRNEQ (name, ELF_STRING_ARM_unwind_once)); } /* Set the type and flags for an ARM section. We do this by the section name, which is a hack, but ought to work. */ static bfd_boolean elf32_arm_fake_sections (bfd * abfd, Elf_Internal_Shdr * hdr, asection * sec) { const char * name; name = bfd_get_section_name (abfd, sec); if (is_arm_elf_unwind_section_name (abfd, name)) { hdr->sh_type = SHT_ARM_EXIDX; hdr->sh_flags |= SHF_LINK_ORDER; } return TRUE; } /* Handle an ARM specific section when reading an object file. This is called when bfd_section_from_shdr finds a section with an unknown type. */ static bfd_boolean elf32_arm_section_from_shdr (bfd *abfd, Elf_Internal_Shdr * hdr, const char *name, int shindex) { /* There ought to be a place to keep ELF backend specific flags, but at the moment there isn't one. We just keep track of the sections by their name, instead. Fortunately, the ABI gives names for all the ARM specific sections, so we will probably get away with this. */ switch (hdr->sh_type) { case SHT_ARM_EXIDX: case SHT_ARM_PREEMPTMAP: case SHT_ARM_ATTRIBUTES: break; default: return FALSE; } if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) return FALSE; return TRUE; } /* A structure used to record a list of sections, independently of the next and prev fields in the asection structure. */ typedef struct section_list { asection * sec; struct section_list * next; struct section_list * prev; } section_list; /* Unfortunately we need to keep a list of sections for which an _arm_elf_section_data structure has been allocated. This is because it is possible for functions like elf32_arm_write_section to be called on a section which has had an elf_data_structure allocated for it (and so the used_by_bfd field is valid) but for which the ARM extended version of this structure - the _arm_elf_section_data structure - has not been allocated. */ static section_list * sections_with_arm_elf_section_data = NULL; static void record_section_with_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = bfd_malloc (sizeof (* entry)); if (entry == NULL) return; entry->sec = sec; entry->next = sections_with_arm_elf_section_data; entry->prev = NULL; if (entry->next != NULL) entry->next->prev = entry; sections_with_arm_elf_section_data = entry; } static struct section_list * find_arm_elf_section_entry (asection * sec) { struct section_list * entry; static struct section_list * last_entry = NULL; /* This is a short cut for the typical case where the sections are added to the sections_with_arm_elf_section_data list in forward order and then looked up here in backwards order. This makes a real difference to the ld-srec/sec64k.exp linker test. */ entry = sections_with_arm_elf_section_data; if (last_entry != NULL) { if (last_entry->sec == sec) entry = last_entry; else if (last_entry->next != NULL && last_entry->next->sec == sec) entry = last_entry->next; } for (; entry; entry = entry->next) if (entry->sec == sec) break; if (entry) /* Record the entry prior to this one - it is the entry we are most likely to want to locate next time. Also this way if we have been called from unrecord_section_with_arm_elf_section_data() we will not be caching a pointer that is about to be freed. */ last_entry = entry->prev; return entry; } static _arm_elf_section_data * get_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = find_arm_elf_section_entry (sec); if (entry) return elf32_arm_section_data (entry->sec); else return NULL; } static void unrecord_section_with_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = find_arm_elf_section_entry (sec); if (entry) { if (entry->prev != NULL) entry->prev->next = entry->next; if (entry->next != NULL) entry->next->prev = entry->prev; if (entry == sections_with_arm_elf_section_data) sections_with_arm_elf_section_data = entry->next; free (entry); } } typedef struct { void *finfo; struct bfd_link_info *info; asection *sec; int sec_shndx; bfd_boolean (*func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *); } output_arch_syminfo; enum map_symbol_type { ARM_MAP_ARM, ARM_MAP_THUMB, ARM_MAP_DATA }; /* Output a single PLT mapping symbol. */ static bfd_boolean elf32_arm_ouput_plt_map_sym (output_arch_syminfo *osi, enum map_symbol_type type, bfd_vma offset) { static const char *names[3] = {"$a", "$t", "$d"}; struct elf32_arm_link_hash_table *htab; Elf_Internal_Sym sym; htab = elf32_arm_hash_table (osi->info); sym.st_value = osi->sec->output_section->vma + osi->sec->output_offset + offset; sym.st_size = 0; sym.st_other = 0; sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE); sym.st_shndx = osi->sec_shndx; if (!osi->func (osi->finfo, names[type], &sym, osi->sec, NULL)) return FALSE; return TRUE; } /* Output mapping symbols for PLT entries associated with H. */ static bfd_boolean elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf) { output_arch_syminfo *osi = (output_arch_syminfo *) inf; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; bfd_vma addr; htab = elf32_arm_hash_table (osi->info); if (h->root.type == bfd_link_hash_indirect) return TRUE; if (h->root.type == bfd_link_hash_warning) /* When warning symbols are created, they **replace** the "real" entry in the hash table, thus we never get to see the real symbol in a hash traversal. So look at it now. */ h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->plt.offset == (bfd_vma) -1) return TRUE; eh = (struct elf32_arm_link_hash_entry *) h; addr = h->plt.offset; if (htab->symbian_p) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 4)) return FALSE; } else if (htab->vxworks_p) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 8)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr + 12)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 20)) return FALSE; } else { bfd_boolean thumb_stub; thumb_stub = eh->plt_thumb_refcount > 0 && !htab->use_blx; if (thumb_stub) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_THUMB, addr - 4)) return FALSE; } #ifdef FOUR_WORD_PLT if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 12)) return FALSE; #else /* A three-word PLT with no Thumb thunk contains only Arm code, so only need to output a mapping symbol for the first PLT entry and entries with thumb thunks. */ if (thumb_stub || addr == 20) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; } #endif } return TRUE; } /* Output mapping symbols for linker generated sections. */ static bfd_boolean elf32_arm_output_arch_local_syms (bfd *output_bfd, struct bfd_link_info *info, void *finfo, bfd_boolean (*func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *)) { output_arch_syminfo osi; struct elf32_arm_link_hash_table *htab; bfd_vma offset; bfd_size_type size; htab = elf32_arm_hash_table (info); check_use_blx(htab); osi.finfo = finfo; osi.info = info; osi.func = func; /* ARM->Thumb glue. */ if (htab->arm_glue_size > 0) { osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, osi.sec->output_section); if (info->shared || htab->root.is_relocatable_executable || htab->pic_veneer) size = ARM2THUMB_PIC_GLUE_SIZE; else if (htab->use_blx) size = ARM2THUMB_V5_STATIC_GLUE_SIZE; else size = ARM2THUMB_STATIC_GLUE_SIZE; for (offset = 0; offset < htab->arm_glue_size; offset += size) { elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset); elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, offset + size - 4); } } /* Thumb->ARM glue. */ if (htab->thumb_glue_size > 0) { osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, osi.sec->output_section); size = THUMB2ARM_GLUE_SIZE; for (offset = 0; offset < htab->thumb_glue_size; offset += size) { elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_THUMB, offset); elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset + 4); } } /* Finally, output mapping symbols for the PLT. */ if (!htab->splt || htab->splt->size == 0) return TRUE; osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, htab->splt->output_section); osi.sec = htab->splt; /* Output mapping symbols for the plt header. SymbianOS does not have a plt header. */ if (htab->vxworks_p) { /* VxWorks shared libraries have no PLT header. */ if (!info->shared) { if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 12)) return FALSE; } } else if (!htab->symbian_p) { if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0)) return FALSE; #ifndef FOUR_WORD_PLT if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 16)) return FALSE; #endif } elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, (void *) &osi); return TRUE; } /* Allocate target specific section data. */ static bfd_boolean elf32_arm_new_section_hook (bfd *abfd, asection *sec) { if (!sec->used_by_bfd) { _arm_elf_section_data *sdata; bfd_size_type amt = sizeof (*sdata); sdata = bfd_zalloc (abfd, amt); if (sdata == NULL) return FALSE; sec->used_by_bfd = sdata; } record_section_with_arm_elf_section_data (sec); return _bfd_elf_new_section_hook (abfd, sec); } /* Used to order a list of mapping symbols by address. */ static int elf32_arm_compare_mapping (const void * a, const void * b) { return ((const elf32_arm_section_map *) a)->vma > ((const elf32_arm_section_map *) b)->vma; } /* Do code byteswapping. Return FALSE afterwards so that the section is written out as normal. */ static bfd_boolean elf32_arm_write_section (bfd *output_bfd, struct bfd_link_info *link_info, asection *sec, bfd_byte *contents) { int mapcount, errcount; _arm_elf_section_data *arm_data; struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); elf32_arm_section_map *map; elf32_vfp11_erratum_list *errnode; bfd_vma ptr; bfd_vma end; bfd_vma offset = sec->output_section->vma + sec->output_offset; bfd_byte tmp; int i; /* If this section has not been allocated an _arm_elf_section_data structure then we cannot record anything. */ arm_data = get_arm_elf_section_data (sec); if (arm_data == NULL) return FALSE; mapcount = arm_data->mapcount; map = arm_data->map; errcount = arm_data->erratumcount; if (errcount != 0) { unsigned int endianflip = bfd_big_endian (output_bfd) ? 3 : 0; for (errnode = arm_data->erratumlist; errnode != 0; errnode = errnode->next) { bfd_vma index = errnode->vma - offset; switch (errnode->type) { case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER: { bfd_vma branch_to_veneer; /* Original condition code of instruction, plus bit mask for ARM B instruction. */ unsigned int insn = (errnode->u.b.vfp_insn & 0xf0000000) | 0x0a000000; /* The instruction is before the label. */ index -= 4; /* Above offset included in -4 below. */ branch_to_veneer = errnode->u.b.veneer->vma - errnode->vma - 4; if ((signed) branch_to_veneer < -(1 << 25) || (signed) branch_to_veneer >= (1 << 25)) (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of " "range"), output_bfd); insn |= (branch_to_veneer >> 2) & 0xffffff; contents[endianflip ^ index] = insn & 0xff; contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff; } break; case VFP11_ERRATUM_ARM_VENEER: { bfd_vma branch_from_veneer; unsigned int insn; /* Take size of veneer into account. */ branch_from_veneer = errnode->u.v.branch->vma - errnode->vma - 12; if ((signed) branch_from_veneer < -(1 << 25) || (signed) branch_from_veneer >= (1 << 25)) (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of " "range"), output_bfd); /* Original instruction. */ insn = errnode->u.v.branch->u.b.vfp_insn; contents[endianflip ^ index] = insn & 0xff; contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff; /* Branch back to insn after original insn. */ insn = 0xea000000 | ((branch_from_veneer >> 2) & 0xffffff); contents[endianflip ^ (index + 4)] = insn & 0xff; contents[endianflip ^ (index + 5)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 6)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 7)] = (insn >> 24) & 0xff; } break; default: abort (); } } } if (mapcount == 0) return FALSE; if (globals->byteswap_code) { qsort (map, mapcount, sizeof (* map), elf32_arm_compare_mapping); ptr = map[0].vma; for (i = 0; i < mapcount; i++) { if (i == mapcount - 1) end = sec->size; else end = map[i + 1].vma; switch (map[i].type) { case 'a': /* Byte swap code words. */ while (ptr + 3 < end) { tmp = contents[ptr]; contents[ptr] = contents[ptr + 3]; contents[ptr + 3] = tmp; tmp = contents[ptr + 1]; contents[ptr + 1] = contents[ptr + 2]; contents[ptr + 2] = tmp; ptr += 4; } break; case 't': /* Byte swap code halfwords. */ while (ptr + 1 < end) { tmp = contents[ptr]; contents[ptr] = contents[ptr + 1]; contents[ptr + 1] = tmp; ptr += 2; } break; case 'd': /* Leave data alone. */ break; } ptr = end; } } free (map); arm_data->mapcount = 0; arm_data->mapsize = 0; arm_data->map = NULL; unrecord_section_with_arm_elf_section_data (sec); return FALSE; } static void unrecord_section_via_map_over_sections (bfd * abfd ATTRIBUTE_UNUSED, asection * sec, void * ignore ATTRIBUTE_UNUSED) { unrecord_section_with_arm_elf_section_data (sec); } static bfd_boolean elf32_arm_close_and_cleanup (bfd * abfd) { if (abfd->sections) bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL); return _bfd_elf_close_and_cleanup (abfd); } static bfd_boolean elf32_arm_bfd_free_cached_info (bfd * abfd) { if (abfd->sections) bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL); return _bfd_free_cached_info (abfd); } /* Display STT_ARM_TFUNC symbols as functions. */ static void elf32_arm_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED, asymbol *asym) { elf_symbol_type *elfsym = (elf_symbol_type *) asym; if (ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_ARM_TFUNC) elfsym->symbol.flags |= BSF_FUNCTION; } /* Mangle thumb function symbols as we read them in. */ static bfd_boolean elf32_arm_swap_symbol_in (bfd * abfd, const void *psrc, const void *pshn, Elf_Internal_Sym *dst) { if (!bfd_elf32_swap_symbol_in (abfd, psrc, pshn, dst)) return FALSE; /* New EABI objects mark thumb function symbols by setting the low bit of the address. Turn these into STT_ARM_TFUNC. */ if (ELF_ST_TYPE (dst->st_info) == STT_FUNC && (dst->st_value & 1)) { dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_ARM_TFUNC); dst->st_value &= ~(bfd_vma) 1; } return TRUE; } /* Mangle thumb function symbols as we write them out. */ static void elf32_arm_swap_symbol_out (bfd *abfd, const Elf_Internal_Sym *src, void *cdst, void *shndx) { Elf_Internal_Sym newsym; /* We convert STT_ARM_TFUNC symbols into STT_FUNC with the low bit of the address set, as per the new EABI. We do this unconditionally because objcopy does not set the elf header flags until after it writes out the symbol table. */ if (ELF_ST_TYPE (src->st_info) == STT_ARM_TFUNC) { newsym = *src; newsym.st_info = ELF_ST_INFO (ELF_ST_BIND (src->st_info), STT_FUNC); if (newsym.st_shndx != SHN_UNDEF) { /* Do this only for defined symbols. At link type, the static linker will simulate the work of dynamic linker of resolving symbols and will carry over the thumbness of found symbols to the output symbol table. It's not clear how it happens, but the thumbness of undefined symbols can well be different at runtime, and writing '1' for them will be confusing for users and possibly for dynamic linker itself. */ newsym.st_value |= 1; } src = &newsym; } bfd_elf32_swap_symbol_out (abfd, src, cdst, shndx); } /* Add the PT_ARM_EXIDX program header. */ static bfd_boolean elf32_arm_modify_segment_map (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED) { struct elf_segment_map *m; asection *sec; sec = bfd_get_section_by_name (abfd, ".ARM.exidx"); if (sec != NULL && (sec->flags & SEC_LOAD) != 0) { /* If there is already a PT_ARM_EXIDX header, then we do not want to add another one. This situation arises when running "strip"; the input binary already has the header. */ m = elf_tdata (abfd)->segment_map; while (m && m->p_type != PT_ARM_EXIDX) m = m->next; if (!m) { m = bfd_zalloc (abfd, sizeof (struct elf_segment_map)); if (m == NULL) return FALSE; m->p_type = PT_ARM_EXIDX; m->count = 1; m->sections[0] = sec; m->next = elf_tdata (abfd)->segment_map; elf_tdata (abfd)->segment_map = m; } } return TRUE; } /* We may add a PT_ARM_EXIDX program header. */ static int elf32_arm_additional_program_headers (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED) { asection *sec; sec = bfd_get_section_by_name (abfd, ".ARM.exidx"); if (sec != NULL && (sec->flags & SEC_LOAD) != 0) return 1; else return 0; } /* We have two function types: STT_FUNC and STT_ARM_TFUNC. */ static bfd_boolean elf32_arm_is_function_type (unsigned int type) { return (type == STT_FUNC) || (type == STT_ARM_TFUNC); } /* We use this to override swap_symbol_in and swap_symbol_out. */ const struct elf_size_info elf32_arm_size_info = { sizeof (Elf32_External_Ehdr), sizeof (Elf32_External_Phdr), sizeof (Elf32_External_Shdr), sizeof (Elf32_External_Rel), sizeof (Elf32_External_Rela), sizeof (Elf32_External_Sym), sizeof (Elf32_External_Dyn), sizeof (Elf_External_Note), 4, 1, 32, 2, ELFCLASS32, EV_CURRENT, bfd_elf32_write_out_phdrs, bfd_elf32_write_shdrs_and_ehdr, bfd_elf32_write_relocs, elf32_arm_swap_symbol_in, elf32_arm_swap_symbol_out, bfd_elf32_slurp_reloc_table, bfd_elf32_slurp_symbol_table, bfd_elf32_swap_dyn_in, bfd_elf32_swap_dyn_out, bfd_elf32_swap_reloc_in, bfd_elf32_swap_reloc_out, bfd_elf32_swap_reloca_in, bfd_elf32_swap_reloca_out }; #define ELF_ARCH bfd_arch_arm #define ELF_MACHINE_CODE EM_ARM #ifdef __QNXTARGET__ #define ELF_MAXPAGESIZE 0x1000 #else #define ELF_MAXPAGESIZE 0x8000 #endif #define ELF_MINPAGESIZE 0x1000 #define ELF_COMMONPAGESIZE 0x1000 #define bfd_elf32_mkobject elf32_arm_mkobject #define bfd_elf32_bfd_copy_private_bfd_data elf32_arm_copy_private_bfd_data #define bfd_elf32_bfd_merge_private_bfd_data elf32_arm_merge_private_bfd_data #define bfd_elf32_bfd_set_private_flags elf32_arm_set_private_flags #define bfd_elf32_bfd_print_private_bfd_data elf32_arm_print_private_bfd_data #define bfd_elf32_bfd_link_hash_table_create elf32_arm_link_hash_table_create #define bfd_elf32_bfd_reloc_type_lookup elf32_arm_reloc_type_lookup #define bfd_elf32_bfd_reloc_name_lookup elf32_arm_reloc_name_lookup #define bfd_elf32_find_nearest_line elf32_arm_find_nearest_line #define bfd_elf32_find_inliner_info elf32_arm_find_inliner_info #define bfd_elf32_new_section_hook elf32_arm_new_section_hook #define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol #define bfd_elf32_close_and_cleanup elf32_arm_close_and_cleanup #define bfd_elf32_bfd_free_cached_info elf32_arm_bfd_free_cached_info #define elf_backend_get_symbol_type elf32_arm_get_symbol_type #define elf_backend_gc_mark_hook elf32_arm_gc_mark_hook #define elf_backend_gc_mark_extra_sections elf32_arm_gc_mark_extra_sections #define elf_backend_gc_sweep_hook elf32_arm_gc_sweep_hook #define elf_backend_check_relocs elf32_arm_check_relocs #define elf_backend_relocate_section elf32_arm_relocate_section #define elf_backend_write_section elf32_arm_write_section #define elf_backend_adjust_dynamic_symbol elf32_arm_adjust_dynamic_symbol #define elf_backend_create_dynamic_sections elf32_arm_create_dynamic_sections #define elf_backend_finish_dynamic_symbol elf32_arm_finish_dynamic_symbol #define elf_backend_finish_dynamic_sections elf32_arm_finish_dynamic_sections #define elf_backend_size_dynamic_sections elf32_arm_size_dynamic_sections #define elf_backend_init_index_section _bfd_elf_init_2_index_sections #define elf_backend_post_process_headers elf32_arm_post_process_headers #define elf_backend_reloc_type_class elf32_arm_reloc_type_class #define elf_backend_object_p elf32_arm_object_p #define elf_backend_section_flags elf32_arm_section_flags #define elf_backend_fake_sections elf32_arm_fake_sections #define elf_backend_section_from_shdr elf32_arm_section_from_shdr #define elf_backend_final_write_processing elf32_arm_final_write_processing #define elf_backend_copy_indirect_symbol elf32_arm_copy_indirect_symbol #define elf_backend_symbol_processing elf32_arm_symbol_processing #define elf_backend_size_info elf32_arm_size_info #define elf_backend_modify_segment_map elf32_arm_modify_segment_map #define elf_backend_additional_program_headers \ elf32_arm_additional_program_headers #define elf_backend_output_arch_local_syms \ elf32_arm_output_arch_local_syms #define elf_backend_begin_write_processing \ elf32_arm_begin_write_processing #define elf_backend_is_function_type elf32_arm_is_function_type #define elf_backend_can_refcount 1 #define elf_backend_can_gc_sections 1 #define elf_backend_plt_readonly 1 #define elf_backend_want_got_plt 1 #define elf_backend_want_plt_sym 0 #define elf_backend_may_use_rel_p 1 #define elf_backend_may_use_rela_p 0 #define elf_backend_default_use_rela_p 0 #define elf_backend_got_header_size 12 #undef elf_backend_obj_attrs_vendor #define elf_backend_obj_attrs_vendor "aeabi" #undef elf_backend_obj_attrs_section #define elf_backend_obj_attrs_section ".ARM.attributes" #undef elf_backend_obj_attrs_arg_type #define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type #undef elf_backend_obj_attrs_section_type #define elf_backend_obj_attrs_section_type SHT_ARM_ATTRIBUTES #include "elf32-target.h" /* VxWorks Targets */ #undef TARGET_LITTLE_SYM #define TARGET_LITTLE_SYM bfd_elf32_littlearm_vxworks_vec #undef TARGET_LITTLE_NAME #define TARGET_LITTLE_NAME "elf32-littlearm-vxworks" #undef TARGET_BIG_SYM #define TARGET_BIG_SYM bfd_elf32_bigarm_vxworks_vec #undef TARGET_BIG_NAME #define TARGET_BIG_NAME "elf32-bigarm-vxworks" /* Like elf32_arm_link_hash_table_create -- but overrides appropriately for VxWorks. */ static struct bfd_link_hash_table * elf32_arm_vxworks_link_hash_table_create (bfd *abfd) { struct bfd_link_hash_table *ret; ret = elf32_arm_link_hash_table_create (abfd); if (ret) { struct elf32_arm_link_hash_table *htab = (struct elf32_arm_link_hash_table *) ret; htab->use_rel = 0; htab->vxworks_p = 1; } return ret; } static void elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker) { elf32_arm_final_write_processing (abfd, linker); elf_vxworks_final_write_processing (abfd, linker); } #undef elf32_bed #define elf32_bed elf32_arm_vxworks_bed #undef bfd_elf32_bfd_link_hash_table_create #define bfd_elf32_bfd_link_hash_table_create \ elf32_arm_vxworks_link_hash_table_create #undef elf_backend_add_symbol_hook #define elf_backend_add_symbol_hook \ elf_vxworks_add_symbol_hook #undef elf_backend_final_write_processing #define elf_backend_final_write_processing \ elf32_arm_vxworks_final_write_processing #undef elf_backend_emit_relocs #define elf_backend_emit_relocs \ elf_vxworks_emit_relocs #undef elf_backend_may_use_rel_p #define elf_backend_may_use_rel_p 0 #undef elf_backend_may_use_rela_p #define elf_backend_may_use_rela_p 1 #undef elf_backend_default_use_rela_p #define elf_backend_default_use_rela_p 1 #undef elf_backend_want_plt_sym #define elf_backend_want_plt_sym 1 #undef ELF_MAXPAGESIZE #define ELF_MAXPAGESIZE 0x1000 #include "elf32-target.h" /* Symbian OS Targets */ #undef TARGET_LITTLE_SYM #define TARGET_LITTLE_SYM bfd_elf32_littlearm_symbian_vec #undef TARGET_LITTLE_NAME #define TARGET_LITTLE_NAME "elf32-littlearm-symbian" #undef TARGET_BIG_SYM #define TARGET_BIG_SYM bfd_elf32_bigarm_symbian_vec #undef TARGET_BIG_NAME #define TARGET_BIG_NAME "elf32-bigarm-symbian" /* Like elf32_arm_link_hash_table_create -- but overrides appropriately for Symbian OS. */ static struct bfd_link_hash_table * elf32_arm_symbian_link_hash_table_create (bfd *abfd) { struct bfd_link_hash_table *ret; ret = elf32_arm_link_hash_table_create (abfd); if (ret) { struct elf32_arm_link_hash_table *htab = (struct elf32_arm_link_hash_table *)ret; /* There is no PLT header for Symbian OS. */ htab->plt_header_size = 0; /* The PLT entries are each three instructions. */ htab->plt_entry_size = 4 * NUM_ELEM (elf32_arm_symbian_plt_entry); htab->symbian_p = 1; /* Symbian uses armv5t or above, so use_blx is always true. */ htab->use_blx = 1; htab->root.is_relocatable_executable = 1; } return ret; } static const struct bfd_elf_special_section elf32_arm_symbian_special_sections[] = { /* In a BPABI executable, the dynamic linking sections do not go in the loadable read-only segment. The post-linker may wish to refer to these sections, but they are not part of the final program image. */ { STRING_COMMA_LEN (".dynamic"), 0, SHT_DYNAMIC, 0 }, { STRING_COMMA_LEN (".dynstr"), 0, SHT_STRTAB, 0 }, { STRING_COMMA_LEN (".dynsym"), 0, SHT_DYNSYM, 0 }, { STRING_COMMA_LEN (".got"), 0, SHT_PROGBITS, 0 }, { STRING_COMMA_LEN (".hash"), 0, SHT_HASH, 0 }, /* These sections do not need to be writable as the SymbianOS postlinker will arrange things so that no dynamic relocation is required. */ { STRING_COMMA_LEN (".init_array"), 0, SHT_INIT_ARRAY, SHF_ALLOC }, { STRING_COMMA_LEN (".fini_array"), 0, SHT_FINI_ARRAY, SHF_ALLOC }, { STRING_COMMA_LEN (".preinit_array"), 0, SHT_PREINIT_ARRAY, SHF_ALLOC }, { NULL, 0, 0, 0, 0 } }; static void elf32_arm_symbian_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info) { /* BPABI objects are never loaded directly by an OS kernel; they are processed by a postlinker first, into an OS-specific format. If the D_PAGED bit is set on the file, BFD will align segments on page boundaries, so that an OS can directly map the file. With BPABI objects, that just results in wasted space. In addition, because we clear the D_PAGED bit, map_sections_to_segments will recognize that the program headers should not be mapped into any loadable segment. */ abfd->flags &= ~D_PAGED; elf32_arm_begin_write_processing(abfd, link_info); } static bfd_boolean elf32_arm_symbian_modify_segment_map (bfd *abfd, struct bfd_link_info *info) { struct elf_segment_map *m; asection *dynsec; /* BPABI shared libraries and executables should have a PT_DYNAMIC segment. However, because the .dynamic section is not marked with SEC_LOAD, the generic ELF code will not create such a segment. */ dynsec = bfd_get_section_by_name (abfd, ".dynamic"); if (dynsec) { for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) if (m->p_type == PT_DYNAMIC) break; if (m == NULL) { m = _bfd_elf_make_dynamic_segment (abfd, dynsec); m->next = elf_tdata (abfd)->segment_map; elf_tdata (abfd)->segment_map = m; } } /* Also call the generic arm routine. */ return elf32_arm_modify_segment_map (abfd, info); } #undef elf32_bed #define elf32_bed elf32_arm_symbian_bed /* The dynamic sections are not allocated on SymbianOS; the postlinker will process them and then discard them. */ #undef ELF_DYNAMIC_SEC_FLAGS #define ELF_DYNAMIC_SEC_FLAGS \ (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED) #undef bfd_elf32_bfd_link_hash_table_create #define bfd_elf32_bfd_link_hash_table_create \ elf32_arm_symbian_link_hash_table_create #undef elf_backend_add_symbol_hook #undef elf_backend_special_sections #define elf_backend_special_sections elf32_arm_symbian_special_sections #undef elf_backend_begin_write_processing #define elf_backend_begin_write_processing \ elf32_arm_symbian_begin_write_processing #undef elf_backend_final_write_processing #define elf_backend_final_write_processing \ elf32_arm_final_write_processing #undef elf_backend_emit_relocs #undef elf_backend_modify_segment_map #define elf_backend_modify_segment_map elf32_arm_symbian_modify_segment_map /* There is no .got section for BPABI objects, and hence no header. */ #undef elf_backend_got_header_size #define elf_backend_got_header_size 0 /* Similarly, there is no .got.plt section. */ #undef elf_backend_want_got_plt #define elf_backend_want_got_plt 0 #undef elf_backend_may_use_rel_p #define elf_backend_may_use_rel_p 1 #undef elf_backend_may_use_rela_p #define elf_backend_may_use_rela_p 0 #undef elf_backend_default_use_rela_p #define elf_backend_default_use_rela_p 0 #undef elf_backend_want_plt_sym #define elf_backend_want_plt_sym 0 #undef ELF_MAXPAGESIZE #define ELF_MAXPAGESIZE 0x8000 #include "elf32-target.h" Index: projects/clang380-import/contrib/binutils =================================================================== --- projects/clang380-import/contrib/binutils (revision 293015) +++ projects/clang380-import/contrib/binutils (revision 293016) Property changes on: projects/clang380-import/contrib/binutils ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/binutils:r292913-293015 Index: projects/clang380-import/contrib/pf =================================================================== --- projects/clang380-import/contrib/pf (revision 293015) +++ projects/clang380-import/contrib/pf (revision 293016) Property changes on: projects/clang380-import/contrib/pf ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/pf:r292913-293015 Index: projects/clang380-import/lib/libmd/mdXhl.c =================================================================== --- projects/clang380-import/lib/libmd/mdXhl.c (revision 293015) +++ projects/clang380-import/lib/libmd/mdXhl.c (revision 293016) @@ -1,113 +1,118 @@ /* mdXhl.c * ---------------------------------------------------------------------------- * "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 * ---------------------------------------------------------------------------- */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "mdX.h" char * MDXEnd(MDX_CTX *ctx, char *buf) { int i; unsigned char digest[LENGTH]; static const char hex[]="0123456789abcdef"; if (!buf) buf = malloc(2*LENGTH + 1); if (!buf) return 0; MDXFinal(digest, ctx); for (i = 0; i < LENGTH; i++) { buf[i+i] = hex[digest[i] >> 4]; buf[i+i+1] = hex[digest[i] & 0x0f]; } buf[i+i] = '\0'; return buf; } char * MDXFile(const char *filename, char *buf) { return (MDXFileChunk(filename, buf, 0, 0)); } char * MDXFileChunk(const char *filename, char *buf, off_t ofs, off_t len) { unsigned char buffer[16*1024]; MDX_CTX ctx; struct stat stbuf; int f, i, e; off_t n; MDXInit(&ctx); f = open(filename, O_RDONLY); if (f < 0) return 0; - if (fstat(f, &stbuf) < 0) - return 0; + if (fstat(f, &stbuf) < 0) { + i = -1; + goto error; + } if (ofs > stbuf.st_size) ofs = stbuf.st_size; if ((len == 0) || (len > stbuf.st_size - ofs)) len = stbuf.st_size - ofs; - if (lseek(f, ofs, SEEK_SET) < 0) - return 0; + if (lseek(f, ofs, SEEK_SET) < 0) { + i = -1; + goto error; + } n = len; i = 0; while (n > 0) { if (n > sizeof(buffer)) i = read(f, buffer, sizeof(buffer)); else i = read(f, buffer, n); if (i <= 0) break; MDXUpdate(&ctx, buffer, i); n -= i; } +error: e = errno; close(f); errno = e; if (i < 0) return 0; return (MDXEnd(&ctx, buf)); } char * MDXData (const void *data, unsigned int len, char *buf) { MDX_CTX ctx; MDXInit(&ctx); MDXUpdate(&ctx,data,len); return (MDXEnd(&ctx, buf)); } #ifdef WEAK_REFS /* When building libmd, provide weak references. Note: this is not activated in the context of compiling these sources for internal use in libcrypt. */ #undef MDXEnd __weak_reference(_libmd_MDXEnd, MDXEnd); #undef MDXFile __weak_reference(_libmd_MDXFile, MDXFile); #undef MDXFileChunk __weak_reference(_libmd_MDXFileChunk, MDXFileChunk); #undef MDXData __weak_reference(_libmd_MDXData, MDXData); #endif Index: projects/clang380-import/lib/libstand/Makefile =================================================================== --- projects/clang380-import/lib/libstand/Makefile (revision 293015) +++ projects/clang380-import/lib/libstand/Makefile (revision 293016) @@ -1,146 +1,152 @@ # $FreeBSD$ # Originally from $NetBSD: Makefile,v 1.21 1997/10/26 22:08:38 lukem Exp $ # # Notes: # - We don't use the libc strerror/sys_errlist because the string table is # quite large. # MK_PROFILE= no MK_SSP= no .include LIBSTAND_SRC= ${.CURDIR} LIBC_SRC= ${LIBSTAND_SRC}/../libc LIB= stand NO_PIC= INCS= stand.h MAN= libstand.3 WARNS?= 0 CFLAGS+= -I${LIBSTAND_SRC} # standalone components and stuff we have modified locally SRCS+= gzguts.h zutil.h __main.c assert.c bcd.c bswap.c environment.c getopt.c gets.c \ globals.c pager.c printf.c strdup.c strerror.c strtol.c strtoul.c random.c \ sbrk.c twiddle.c zalloc.c zalloc_malloc.c # private (pruned) versions of libc string functions SRCS+= strcasecmp.c .PATH: ${LIBC_SRC}/net SRCS+= ntoh.c # string functions from libc .PATH: ${LIBC_SRC}/string SRCS+= bcmp.c bcopy.c bzero.c ffs.c memccpy.c memchr.c memcmp.c memcpy.c \ memmove.c memset.c qdivrem.c strcat.c strchr.c strcmp.c strcpy.c \ strcspn.c strlcat.c strlcpy.c strlen.c strncat.c strncmp.c strncpy.c \ strpbrk.c strrchr.c strsep.c strspn.c strstr.c strtok.c swab.c .if ${MACHINE_CPUARCH} == "arm" .PATH: ${LIBC_SRC}/arm/gen +# Do not generate movt/movw, because the relocation fixup for them does not +# translate to the -Bsymbolic -pie format required by self_reloc() in loader(8). +# Also, the fpu is not available in a standalone environment. +CFLAGS.clang+= -mllvm -arm-use-movt=0 +CFLAGS.clang+= -mfpu=none + # Compiler support functions .PATH: ${LIBSTAND_SRC}/../../contrib/compiler-rt/lib/builtins/ # __clzsi2 and ctzsi2 for various builtin functions SRCS+= clzsi2.c ctzsi2.c # Divide and modulus functions called by the compiler SRCS+= divmoddi4.c divmodsi4.c divdi3.c divsi3.c moddi3.c modsi3.c SRCS+= udivmoddi4.c udivmodsi4.c udivdi3.c udivsi3.c umoddi3.c umodsi3.c .PATH: ${LIBSTAND_SRC}/../../contrib/compiler-rt/lib/builtins/arm/ SRCS+= aeabi_idivmod.S aeabi_ldivmod.S aeabi_uidivmod.S aeabi_uldivmod.S SRCS+= aeabi_memcmp.S aeabi_memcpy.S aeabi_memmove.S aeabi_memset.S .endif .if ${MACHINE_CPUARCH} == "aarch64" .PATH: ${LIBC_SRC}/aarch64/gen .endif .if ${MACHINE_CPUARCH} == "powerpc" .PATH: ${LIBC_SRC}/quad SRCS+= ashldi3.c ashrdi3.c SRCS+= syncicache.c .endif # uuid functions from libc .PATH: ${LIBC_SRC}/uuid SRCS+= uuid_create_nil.c uuid_equal.c uuid_from_string.c uuid_is_nil.c uuid_to_string.c # _setjmp/_longjmp .PATH: ${LIBSTAND_SRC}/${MACHINE_CPUARCH} SRCS+= _setjmp.S # decompression functionality from libbz2 # NOTE: to actually test this functionality after libbz2 upgrade compile # loader(8) with LOADER_BZIP2_SUPPORT defined .PATH: ${LIBSTAND_SRC}/../../contrib/bzip2 CFLAGS+= -DBZ_NO_STDIO -DBZ_NO_COMPRESS SRCS+= libstand_bzlib_private.h .for file in bzlib.c crctable.c decompress.c huffman.c randtable.c SRCS+= _${file} CLEANFILES+= _${file} _${file}: ${file} sed "s|bzlib_private\.h|libstand_bzlib_private.h|" \ ${.ALLSRC} > ${.TARGET} .endfor CLEANFILES+= libstand_bzlib_private.h libstand_bzlib_private.h: bzlib_private.h sed -e 's||"stand.h"|' \ ${.ALLSRC} > ${.TARGET} # decompression functionality from libz .PATH: ${LIBSTAND_SRC}/../libz CFLAGS+=-DHAVE_MEMCPY -I${LIBSTAND_SRC}/../libz SRCS+= adler32.c crc32.c libstand_zutil.h libstand_gzguts.h .for file in infback.c inffast.c inflate.c inftrees.c zutil.c SRCS+= _${file} CLEANFILES+= _${file} _${file}: ${file} sed -e "s|zutil\.h|libstand_zutil.h|" \ -e "s|gzguts\.h|libstand_gzguts.h|" \ ${.ALLSRC} > ${.TARGET} .endfor # depend on stand.h being able to be included multiple times .for file in zutil.h gzguts.h CLEANFILES+= libstand_${file} libstand_${file}: ${file} sed -e 's||"stand.h"|' \ -e 's||"stand.h"|' \ -e 's||"stand.h"|' \ -e 's||"stand.h"|' \ -e 's||"stand.h"|' \ ${.ALLSRC} > ${.TARGET} .endfor # io routines SRCS+= closeall.c dev.c ioctl.c nullfs.c stat.c \ fstat.c close.c lseek.c open.c read.c write.c readdir.c # network routines SRCS+= arp.c ether.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c # network info services: SRCS+= bootp.c rarp.c bootparam.c # boot filesystems SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c SRCS+= dosfs.c ext2fs.c SRCS+= splitfs.c SRCS+= pkgfs.c .if ${MK_NAND} != "no" SRCS+= nandfs.c .endif .include .include Index: projects/clang380-import/share/man/man4/Makefile =================================================================== --- projects/clang380-import/share/man/man4/Makefile (revision 293015) +++ projects/clang380-import/share/man/man4/Makefile (revision 293016) @@ -1,942 +1,944 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD$ .include MAN= aac.4 \ aacraid.4 \ acpi.4 \ ${_acpi_asus.4} \ ${_acpi_asus_wmi.4} \ ${_acpi_dock.4} \ ${_acpi_fujitsu.4} \ ${_acpi_hp.4} \ ${_acpi_ibm.4} \ ${_acpi_panasonic.4} \ ${_acpi_rapidstart.4} \ ${_acpi_sony.4} \ acpi_thermal.4 \ ${_acpi_toshiba.4} \ acpi_video.4 \ ${_acpi_wmi.4} \ ada.4 \ adv.4 \ adw.4 \ ae.4 \ ${_aesni.4} \ age.4 \ agp.4 \ aha.4 \ ahb.4 \ ahc.4 \ ahci.4 \ ahd.4 \ ${_aibs.4} \ aio.4 \ alc.4 \ ale.4 \ alpm.4 \ altera_atse.4 \ altera_avgen.4 \ altera_jtag_uart.4 \ altera_sdcard.4 \ altq.4 \ amdpm.4 \ ${_amdsbwd.4} \ ${_amdsmb.4} \ ${_amdtemp.4} \ ${_bxe.4} \ amr.4 \ an.4 \ ${_aout.4} \ ${_apic.4} \ arcmsr.4 \ ${_asmc.4} \ ata.4 \ ath.4 \ ath_ahb.4 \ ath_hal.4 \ ath_pci.4 \ atkbd.4 \ atkbdc.4 \ atp.4 \ ${_atf_test_case.4} \ ${_atrtc.4} \ ${_attimer.4} \ audit.4 \ auditpipe.4 \ aue.4 \ axe.4 \ axge.4 \ bce.4 \ bfe.4 \ bge.4 \ ${_bhyve.4} \ bktr.4 \ blackhole.4 \ bpf.4 \ bridge.4 \ bt.4 \ bwi.4 \ bwn.4 \ capsicum.4 \ cardbus.4 \ carp.4 \ cas.4 \ cc_cdg.4 \ cc_chd.4 \ cc_cubic.4 \ cc_dctcp.4 \ cc_hd.4 \ cc_htcp.4 \ cc_newreno.4 \ cc_vegas.4 \ ${_ccd.4} \ cd.4 \ cdce.4 \ ch.4 \ ciss.4 \ cloudabi.4 \ cm.4 \ cmx.4 \ ${_coretemp.4} \ ${_cpuctl.4} \ cpufreq.4 \ crypto.4 \ ctl.4 \ cue.4 \ cxgb.4 \ cxgbe.4 \ cy.4 \ cyapa.4 \ da.4 \ dc.4 \ dcons.4 \ dcons_crom.4 \ ddb.4 \ de.4 \ devctl.4 \ digi.4 \ disc.4 \ divert.4 \ ${_dpms.4} \ dpt.4 \ ds1307.4 \ ds3231.4 \ ${_dtrace_io.4} \ ${_dtrace_ip.4} \ ${_dtrace_proc.4} \ ${_dtrace_sched.4} \ ${_dtrace_tcp.4} \ ${_dtrace_udp.4} \ dummynet.4 \ ed.4 \ edsc.4 \ ehci.4 \ em.4 \ en.4 \ enc.4 \ epair.4 \ esp.4 \ est.4 \ et.4 \ etherswitch.4 \ eventtimers.4 \ exca.4 \ fatm.4 \ fd.4 \ fdc.4 \ fdt.4 \ fdtbus.4 \ ffclock.4 \ filemon.4 \ firewire.4 \ fpa.4 \ full.4 \ fwe.4 \ fwip.4 \ fwohci.4 \ fxp.4 \ gbde.4 \ gdb.4 \ gem.4 \ geom.4 \ geom_fox.4 \ geom_linux_lvm.4 \ geom_map.4 \ geom_uncompress.4 \ geom_uzip.4 \ gif.4 \ gpio.4 \ gpioiic.4 \ gpioled.4 \ gre.4 \ h_ertt.4 \ hatm.4 \ hifn.4 \ hme.4 \ hpet.4 \ ${_hpt27xx.4} \ ${_hptiop.4} \ ${_hptmv.4} \ ${_hptnr.4} \ ${_hptrr.4} \ hv_ata_pci_disengage.4 \ hv_kvp.4 \ hv_netvsc.4 \ hv_storvsc.4 \ hv_utils.4 \ hv_vmbus.4 \ hwpmc.4 \ ichsmb.4 \ ${_ichwd.4} \ icmp.4 \ icmp6.4 \ ida.4 \ ifmib.4 \ ig4.4 \ igb.4 \ igmp.4 \ iic.4 \ iicbb.4 \ iicbus.4 \ iicsmb.4 \ iir.4 \ inet.4 \ inet6.4 \ intpm.4 \ intro.4 \ ${_io.4} \ ${_ioat.4} \ ip.4 \ ip6.4 \ ipfirewall.4 \ ipheth.4 \ ${_ipmi.4} \ ips.4 \ ipsec.4 \ ipw.4 \ ipwfw.4 \ isci.4 \ isl.4 \ ismt.4 \ isp.4 \ ispfw.4 \ iwi.4 \ iwifw.4 \ iwm.4 \ iwmfw.4 \ iwn.4 \ iwnfw.4 \ ixgb.4 \ ixgbe.4 \ ixl.4 \ ixlv.4 \ jme.4 \ joy.4 \ kbdmux.4 \ keyboard.4 \ kld.4 \ ksyms.4 \ ktr.4 \ kue.4 \ lagg.4 \ le.4 \ led.4 \ lge.4 \ ${_linux.4} \ lm75.4 \ lmc.4 \ lo.4 \ lp.4 \ lpbb.4 \ lpt.4 \ mac.4 \ mac_biba.4 \ mac_bsdextended.4 \ mac_ifoff.4 \ mac_lomac.4 \ mac_mls.4 \ mac_none.4 \ mac_partition.4 \ mac_portacl.4 \ mac_seeotheruids.4 \ mac_stub.4 \ mac_test.4 \ malo.4 \ mcd.4 \ md.4 \ mdio.4 \ me.4 \ mem.4 \ meteor.4 \ mfi.4 \ miibus.4 \ mk48txx.4 \ mld.4 \ mlx.4 \ mlx5en.4 \ mly.4 \ mmc.4 \ mmcsd.4 \ mn.4 \ mod_cc.4 \ mos.4 \ mouse.4 \ mpr.4 \ mps.4 \ mpt.4 \ mrsas.4 \ msk.4 \ mtio.4 \ multicast.4 \ mvs.4 \ mwl.4 \ mwlfw.4 \ mxge.4 \ my.4 \ nand.4 \ nandsim.4 \ natm.4 \ natmip.4 \ ncr.4 \ ncv.4 \ ${_ndis.4} \ net80211.4 \ netfpga10g_nf10bmac.4 \ netgraph.4 \ netintro.4 \ netmap.4 \ ${_nfe.4} \ ${_nfsmb.4} \ ng_async.4 \ ng_atm.4 \ ngatmbase.4 \ ng_atmllc.4 \ ng_bpf.4 \ ng_bridge.4 \ ng_bt3c.4 \ ng_btsocket.4 \ ng_car.4 \ ng_ccatm.4 \ ng_cisco.4 \ ng_deflate.4 \ ng_device.4 \ nge.4 \ ng_echo.4 \ ng_eiface.4 \ ng_etf.4 \ ng_ether.4 \ ng_ether_echo.4 \ ng_frame_relay.4 \ ng_gif.4 \ ng_gif_demux.4 \ ng_h4.4 \ ng_hci.4 \ ng_hole.4 \ ng_hub.4 \ ng_iface.4 \ ng_ipfw.4 \ ng_ip_input.4 \ ng_ksocket.4 \ ng_l2cap.4 \ ng_l2tp.4 \ ng_lmi.4 \ ng_mppc.4 \ ng_nat.4 \ ng_netflow.4 \ ng_one2many.4 \ ng_patch.4 \ ng_ppp.4 \ ng_pppoe.4 \ ng_pptpgre.4 \ ng_pred1.4 \ ng_rfc1490.4 \ ng_socket.4 \ ng_source.4 \ ng_split.4 \ ng_sppp.4 \ ng_sscfu.4 \ ng_sscop.4 \ ng_tag.4 \ ng_tcpmss.4 \ ng_tee.4 \ ng_tty.4 \ ng_ubt.4 \ ng_UI.4 \ ng_uni.4 \ ng_vjc.4 \ ng_vlan.4 \ nmdm.4 \ nsp.4 \ ${_ntb.4} \ null.4 \ numa.4 \ ${_nvd.4} \ ${_nvme.4} \ ${_nvram.4} \ ${_nvram2env.4} \ ${_nxge.4} \ oce.4 \ ohci.4 \ orm.4 \ ow.4 \ ow_temp.4 \ owc.4 \ ${_padlock.4} \ pass.4 \ patm.4 \ pccard.4 \ pccbb.4 \ pcf.4 \ pci.4 \ pcib.4 \ pcic.4 \ pcm.4 \ pcn.4 \ ${_pf.4} \ ${_pflog.4} \ ${_pfsync.4} \ pim.4 \ pms.4 \ polling.4 \ ppbus.4 \ ppc.4 \ ppi.4 \ procdesc.4 \ proto.4 \ psm.4 \ pst.4 \ pt.4 \ pts.4 \ pty.4 \ puc.4 \ ${_qlxge.4} \ ${_qlxgb.4} \ ${_qlxgbe.4} \ ral.4 \ random.4 \ rc.4 \ re.4 \ rgephy.4 \ rights.4 \ rl.4 \ rndtest.4 \ route.4 \ rp.4 \ + rtwn.4 \ + rtwnfw.4 \ rue.4 \ rum.4 \ run.4 \ runfw.4 \ sa.4 \ safe.4 \ sbp.4 \ sbp_targ.4 \ scc.4 \ scd.4 \ sched_4bsd.4 \ sched_ule.4 \ screen.4 \ scsi.4 \ sctp.4 \ sdhci.4 \ sem.4 \ send.4 \ ses.4 \ sf.4 \ ${_sfxge.4} \ sge.4 \ si.4 \ siba.4 \ siftr.4 \ siis.4 \ simplebus.4 \ sio.4 \ sis.4 \ sk.4 \ smb.4 \ smbus.4 \ smp.4 \ smsc.4 \ sn.4 \ snd_ad1816.4 \ snd_als4000.4 \ snd_atiixp.4 \ snd_cmi.4 \ snd_cs4281.4 \ snd_csa.4 \ snd_ds1.4 \ snd_emu10k1.4 \ snd_emu10kx.4 \ snd_envy24.4 \ snd_envy24ht.4 \ snd_es137x.4 \ snd_ess.4 \ snd_fm801.4 \ snd_gusc.4 \ snd_hda.4 \ snd_hdspe.4 \ snd_ich.4 \ snd_maestro3.4 \ snd_maestro.4 \ snd_mss.4 \ snd_neomagic.4 \ snd_sbc.4 \ snd_solo.4 \ snd_spicds.4 \ snd_t4dwave.4 \ snd_uaudio.4 \ snd_via8233.4 \ snd_via82c686.4 \ snd_vibes.4 \ snp.4 \ spic.4 \ ${_spkr.4} \ splash.4 \ sppp.4 \ ste.4 \ stf.4 \ stg.4 \ stge.4 \ sym.4 \ syncache.4 \ syncer.4 \ syscons.4 \ sysmouse.4 \ tap.4 \ targ.4 \ tcp.4 \ tdfx.4 \ terasic_mtl.4 \ termios.4 \ textdump.4 \ ti.4 \ timecounters.4 \ tl.4 \ ${_tpm.4} \ trm.4 \ tty.4 \ tun.4 \ twa.4 \ twe.4 \ tws.4 \ tx.4 \ txp.4 \ ure.4 \ vale.4 \ vga.4 \ vge.4 \ viapm.4 \ ${_viawd.4} \ ${_virtio.4} \ ${_virtio_balloon.4} \ ${_virtio_blk.4} \ ${_virtio_console.4} \ ${_virtio_random.4} \ ${_virtio_scsi.4} \ vkbd.4 \ vlan.4 \ vxlan.4 \ ${_vmx.4} \ vpo.4 \ vr.4 \ vt.4 \ vte.4 \ ${_vtnet.4} \ ${_vxge.4} \ watchdog.4 \ wb.4 \ ${_wbwd.4} \ wi.4 \ witness.4 \ wlan.4 \ wlan_acl.4 \ wlan_amrr.4 \ wlan_ccmp.4 \ wlan_tkip.4 \ wlan_wep.4 \ wlan_xauth.4 \ ${_wpi.4} \ wsp.4 \ xe.4 \ ${_xen.4} \ xhci.4 \ xl.4 \ ${_xnb.4} \ xpt.4 \ zero.4 \ zyd.4 MLINKS= ae.4 if_ae.4 MLINKS+=age.4 if_age.4 MLINKS+=agp.4 agpgart.4 MLINKS+=alc.4 if_alc.4 MLINKS+=ale.4 if_ale.4 MLINKS+=altera_atse.4 atse.4 MLINKS+=altera_sdcard.4 altera_sdcardc.4 MLINKS+=altq.4 ALTQ.4 MLINKS+=ath.4 if_ath.4 MLINKS+=ath_pci.4 if_ath_pci.4 MLINKS+=an.4 if_an.4 MLINKS+=aue.4 if_aue.4 MLINKS+=axe.4 if_axe.4 MLINKS+=bce.4 if_bce.4 MLINKS+=bfe.4 if_bfe.4 MLINKS+=bge.4 if_bge.4 MLINKS+=bktr.4 brooktree.4 MLINKS+=bridge.4 if_bridge.4 MLINKS+=bwi.4 if_bwi.4 MLINKS+=bwn.4 if_bwn.4 MLINKS+=${_bxe.4} ${_if_bxe.4} MLINKS+=cas.4 if_cas.4 MLINKS+=cdce.4 if_cdce.4 MLINKS+=cloudabi.4 cloudabi64.4 MLINKS+=crypto.4 cryptodev.4 MLINKS+=cue.4 if_cue.4 MLINKS+=cxgb.4 if_cxgb.4 MLINKS+=cxgbe.4 if_cxgbe.4 \ cxgbe.4 cxl.4 \ cxgbe.4 if_cxl.4 MLINKS+=dc.4 if_dc.4 MLINKS+=de.4 if_de.4 MLINKS+=disc.4 if_disc.4 MLINKS+=ed.4 if_ed.4 MLINKS+=edsc.4 if_edsc.4 MLINKS+=em.4 if_em.4 MLINKS+=en.4 if_en.4 MLINKS+=enc.4 if_enc.4 MLINKS+=epair.4 if_epair.4 MLINKS+=et.4 if_et.4 MLINKS+=fatm.4 if_fatm.4 MLINKS+=fd.4 stderr.4 \ fd.4 stdin.4 \ fd.4 stdout.4 MLINKS+=fdt.4 FDT.4 MLINKS+=firewire.4 ieee1394.4 MLINKS+=fpa.4 fea.4 MLINKS+=fwe.4 if_fwe.4 MLINKS+=fwip.4 if_fwip.4 MLINKS+=fxp.4 if_fxp.4 MLINKS+=gem.4 if_gem.4 MLINKS+=geom.4 GEOM.4 MLINKS+=gif.4 if_gif.4 MLINKS+=gpio.4 gpiobus.4 MLINKS+=gre.4 if_gre.4 MLINKS+=hatm.4 if_hatm.4 MLINKS+=hme.4 if_hme.4 MLINKS+=hpet.4 acpi_hpet.4 MLINKS+=${_hptrr.4} ${_rr232x.4} MLINKS+=${_attimer.4} ${_i8254.4} MLINKS+=igb.4 if_igb.4 MLINKS+=ip.4 rawip.4 MLINKS+=ipfirewall.4 ipaccounting.4 \ ipfirewall.4 ipacct.4 \ ipfirewall.4 ipfw.4 MLINKS+=ipheth.4 if_ipheth.4 MLINKS+=ipw.4 if_ipw.4 MLINKS+=iwi.4 if_iwi.4 MLINKS+=iwm.4 if_iwm.4 MLINKS+=iwn.4 if_iwn.4 MLINKS+=ixgb.4 if_ixgb.4 MLINKS+=ixgbe.4 ix.4 MLINKS+=ixgbe.4 if_ix.4 MLINKS+=ixgbe.4 if_ixgbe.4 MLINKS+=ixl.4 if_ixl.4 MLINKS+=ixlv.4 if_ixlv.4 MLINKS+=jme.4 if_jme.4 MLINKS+=kue.4 if_kue.4 MLINKS+=lagg.4 trunk.4 MLINKS+=lagg.4 if_lagg.4 MLINKS+=le.4 if_le.4 MLINKS+=lge.4 if_lge.4 MLINKS+=lmc.4 if_lmc.4 MLINKS+=lo.4 loop.4 MLINKS+=lp.4 plip.4 MLINKS+=malo.4 if_malo.4 MLINKS+=md.4 vn.4 MLINKS+=mem.4 kmem.4 MLINKS+=mlx5en.4 mce.4 MLINKS+=mn.4 if_mn.4 MLINKS+=mos.4 if_mos.4 MLINKS+=msk.4 if_msk.4 MLINKS+=mwl.4 if_mwl.4 MLINKS+=mxge.4 if_mxge.4 MLINKS+=my.4 if_my.4 MLINKS+=${_ndis.4} ${_if_ndis.4} MLINKS+=netfpga10g_nf10bmac.4 if_nf10bmac.4 MLINKS+=netintro.4 net.4 \ netintro.4 networking.4 MLINKS+=${_nfe.4} ${_if_nfe.4} MLINKS+=nge.4 if_nge.4 MLINKS+=${_ntb.4} ${_if_ntb.4} \ ${_ntb.4} ${_ntb_hw.4} MLINKS+=${_nxge.4} ${_if_nxge.4} MLINKS+=ow.4 onewire.4 MLINKS+=patm.4 if_patm.4 MLINKS+=pccbb.4 cbb.4 MLINKS+=pcm.4 snd.4 \ pcm.4 sound.4 MLINKS+=pcn.4 if_pcn.4 MLINKS+=pms.4 pmspcv.4 MLINKS+=ral.4 if_ral.4 MLINKS+=re.4 if_re.4 MLINKS+=rl.4 if_rl.4 MLINKS+=rue.4 if_rue.4 MLINKS+=rum.4 if_rum.4 MLINKS+=run.4 if_run.4 MLINKS+=scsi.4 CAM.4 \ scsi.4 cam.4 \ scsi.4 scbus.4 \ scsi.4 SCSI.4 MLINKS+=sf.4 if_sf.4 MLINKS+=sge.4 if_sge.4 MLINKS+=sis.4 if_sis.4 MLINKS+=sk.4 if_sk.4 MLINKS+=smp.4 SMP.4 MLINKS+=smsc.4 if_smsc.4 MLINKS+=sn.4 if_sn.4 MLINKS+=snd_envy24.4 snd_ak452x.4 MLINKS+=snd_sbc.4 snd_sb16.4 \ snd_sbc.4 snd_sb8.4 MLINKS+=${_spkr.4} ${_speaker.4} MLINKS+=splash.4 screensaver.4 MLINKS+=ste.4 if_ste.4 MLINKS+=stf.4 if_stf.4 MLINKS+=stge.4 if_stge.4 MLINKS+=syncache.4 syncookies.4 MLINKS+=syscons.4 sc.4 MLINKS+=tap.4 if_tap.4 MLINKS+=tdfx.4 tdfx_linux.4 MLINKS+=ti.4 if_ti.4 MLINKS+=tl.4 if_tl.4 MLINKS+=tun.4 if_tun.4 MLINKS+=tx.4 if_tx.4 MLINKS+=txp.4 if_txp.4 MLINKS+=ure.4 if_ure.4 MLINKS+=vge.4 if_vge.4 MLINKS+=vlan.4 if_vlan.4 MLINKS+=vxlan.4 if_vxlan.4 MLINKS+=${_vmx.4} ${_if_vmx.4} MLINKS+=vpo.4 imm.4 MLINKS+=vr.4 if_vr.4 MLINKS+=vte.4 if_vte.4 MLINKS+=${_vtnet.4} ${_if_vtnet.4} MLINKS+=${_vxge.4} ${_if_vxge.4} MLINKS+=watchdog.4 SW_WATCHDOG.4 MLINKS+=wb.4 if_wb.4 MLINKS+=wi.4 if_wi.4 MLINKS+=${_wpi.4} ${_if_wpi.4} MLINKS+=xe.4 if_xe.4 MLINKS+=xl.4 if_xl.4 MLINKS+=zyd.4 if_zyd.4 .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" _acpi_asus.4= acpi_asus.4 _acpi_asus_wmi.4= acpi_asus_wmi.4 _acpi_dock.4= acpi_dock.4 _acpi_fujitsu.4=acpi_fujitsu.4 _acpi_hp.4= acpi_hp.4 _acpi_ibm.4= acpi_ibm.4 _acpi_panasonic.4=acpi_panasonic.4 _acpi_rapidstart.4=acpi_rapidstart.4 _acpi_sony.4= acpi_sony.4 _acpi_toshiba.4=acpi_toshiba.4 _acpi_wmi.4= acpi_wmi.4 _aesni.4= aesni.4 _aout.4= aout.4 _apic.4= apic.4 _atrtc.4= atrtc.4 _attimer.4= attimer.4 _aibs.4= aibs.4 _amdsbwd.4= amdsbwd.4 _amdsmb.4= amdsmb.4 _amdtemp.4= amdtemp.4 _asmc.4= asmc.4 _bxe.4= bxe.4 _coretemp.4= coretemp.4 _cpuctl.4= cpuctl.4 _dpms.4= dpms.4 _hpt27xx.4= hpt27xx.4 _hptiop.4= hptiop.4 _hptmv.4= hptmv.4 _hptnr.4= hptnr.4 _hptrr.4= hptrr.4 _i8254.4= i8254.4 _ichwd.4= ichwd.4 _if_bxe.4= if_bxe.4 _if_ndis.4= if_ndis.4 _if_nfe.4= if_nfe.4 _if_nxge.4= if_nxge.4 _if_urtw.4= if_urtw.4 _if_vmx.4= if_vmx.4 _if_vtnet.4= if_vtnet.4 _if_vxge.4= if_vxge.4 _if_wpi.4= if_wpi.4 _ipmi.4= ipmi.4 _io.4= io.4 _linux.4= linux.4 _ndis.4= ndis.4 _nfe.4= nfe.4 _nfsmb.4= nfsmb.4 _nvd.4= nvd.4 _nvme.4= nvme.4 _nvram.4= nvram.4 _nxge.4= nxge.4 _virtio.4= virtio.4 _virtio_balloon.4=virtio_balloon.4 _virtio_blk.4= virtio_blk.4 _virtio_console.4=virtio_console.4 _virtio_random.4= virtio_random.4 _virtio_scsi.4= virtio_scsi.4 _vmx.4= vmx.4 _vtnet.4= vtnet.4 _vxge.4= vxge.4 _padlock.4= padlock.4 _rr232x.4= rr232x.4 _speaker.4= speaker.4 _spkr.4= spkr.4 _tpm.4= tpm.4 _urtw.4= urtw.4 _viawd.4= viawd.4 _wbwd.4= wbwd.4 _wpi.4= wpi.4 _xen.4= xen.4 _xnb.4= xnb.4 .endif .if ${MACHINE_CPUARCH} == "amd64" _if_ntb.4= if_ntb.4 _ioat.4= ioat.4 _ntb.4= ntb.4 _ntb_hw.4= ntb_hw.4 _qlxge.4= qlxge.4 _qlxgb.4= qlxgb.4 _qlxgbe.4= qlxgbe.4 _sfxge.4= sfxge.4 MLINKS+=qlxge.4 if_qlxge.4 MLINKS+=qlxgb.4 if_qlxgb.4 MLINKS+=qlxgbe.4 if_qlxgbe.4 MLINKS+=sfxge.4 if_sfxge.4 .if ${MK_BHYVE} != "no" _bhyve.4= bhyve.4 .endif .endif .if ${MACHINE_CPUARCH} == "mips" _nvram2env.4= nvram2env.4 .endif .if exists(${.CURDIR}/man4.${MACHINE_CPUARCH}) SUBDIR= man4.${MACHINE_CPUARCH} .endif .if ${MK_BLUETOOTH} != "no" MAN+= ng_bluetooth.4 .endif .if ${MK_CCD} != "no" _ccd.4= ccd.4 .endif .if ${MK_CDDL} != "no" _dtrace_io.4= dtrace_io.4 _dtrace_ip.4= dtrace_ip.4 _dtrace_proc.4= dtrace_proc.4 _dtrace_sched.4= dtrace_sched.4 _dtrace_tcp.4= dtrace_tcp.4 _dtrace_udp.4= dtrace_udp.4 .endif .if ${MK_ISCSI} != "no" MAN+= iscsi.4 MAN+= iscsi_initiator.4 .endif .if ${MK_TESTS} != "no" ATF= ${.CURDIR}/../../../contrib/atf .PATH: ${ATF}/doc _atf_test_case.4= atf-test-case.4 .endif .if ${MK_PF} != "no" _pf.4= pf.4 _pflog.4= pflog.4 _pfsync.4= pfsync.4 .endif .if ${MK_USB} != "no" MAN+= \ otus.4 \ otusfw.4 \ rsu.4 \ rsufw.4 \ u3g.4 \ uark.4 \ uart.4 \ uath.4 \ ubsa.4 \ ubsec.4 \ ubser.4 \ ubtbcmfw.4 \ uchcom.4 \ ucom.4 \ ucycom.4 \ udav.4 \ udbp.4 \ udp.4 \ udplite.4 \ udl.4 \ uep.4 \ ufm.4 \ ufoma.4 \ uftdi.4 \ ugen.4 \ ugold.4 \ uhci.4 \ uhid.4 \ uhso.4 \ uipaq.4 \ ukbd.4 \ uled.4 \ ulpt.4 \ umass.4 \ umcs.4 \ umct.4 \ umodem.4 \ umoscom.4 \ ums.4 \ unix.4 \ upgt.4 \ uplcom.4 \ ural.4 \ urio.4 \ urndis.4 \ ${_urtw.4} \ urtwn.4 \ urtwnfw.4 \ usb.4 \ usb_quirk.4 \ usb_template.4 \ usfs.4 \ uslcom.4 \ utopia.4 \ uvisor.4 \ uvscom.4 \ MLINKS+=otus.4 if_otus.4 MLINKS+=rsu.4 if_rsu.4 MLINKS+=u3g.4 u3gstub.4 MLINKS+=uath.4 if_uath.4 MLINKS+=udav.4 if_udav.4 MLINKS+=upgt.4 if_upgt.4 MLINKS+=ural.4 if_ural.4 MLINKS+=urndis.4 if_urndis.4 MLINKS+=${_urtw.4} ${_if_urtw.4} MLINKS+=urtwn.4 if_urtwn.4 .endif .include Index: projects/clang380-import/share/man/man4/rtwn.4 =================================================================== --- projects/clang380-import/share/man/man4/rtwn.4 (nonexistent) +++ projects/clang380-import/share/man/man4/rtwn.4 (revision 293016) @@ -0,0 +1,165 @@ +.\" $OpenBSD: rtwn.4,v 1.2 2015/07/09 11:28:53 stsp Exp $ +.\" +.\" Copyright (c) 2010 Damien Bergamini +.\" Copyright (c) 2015 Stefan Sperling +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 28, 2015 +.Dt RTWN 4 +.Os +.Sh NAME +.Nm rtwn +.Nd Realtek RTL8188CE PCIe IEEE 802.11b/g/n wireless network device +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device rtwn" +.Cd "device rtwnfw" +.Cd "device wlan" +.Cd "device firmware" +.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_rtwn_load="YES" +.Ed +.Pp +After you have read the license in +.Pa /usr/share/doc/legal/realtek.LICENSE +you will want to add the following lines to +.Xr loader.conf 5 : +.Bd -literal -offset indent +legal.realtek.license_ack=1 +rtwn-rtl8192cfwU_load="YES" +rtwn-rtl8192cfwU_B_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports PCIe wireless network devices based on the Realtek +RTL8188CE chipset. +.Pp +The RTL8188CE is a highly integrated 802.11n adapter that combines a MAC, +a 1T1R capable baseband and an RF in a single chip. +It operates in the 2GHz spectrum only. +.Pp +These are the modes the +.Nm +driver can operate in: +.Bl -tag -width "IBSS-masterXX" +.It BSS mode +Also known as +.Em infrastructure +mode, this is used when associating with an access point, through +which all traffic passes. +This mode is the default. +.It monitor mode +In this mode the driver is able to receive packets without +associating with an access point. +This disables the internal receive filter and enables the card to +capture packets from networks which it wouldn't normally have access to, +or to scan for access points. +.El +.Pp +The +.Nm +driver can be configured to use +Wired Equivalent Privacy (WEP) or +Wi-Fi Protected Access (WPA-PSK and WPA2-PSK). +WPA is the current encryption standard for wireless networks. +It is strongly recommended that WEP +not be used as the sole mechanism +to secure wireless communication, +due to serious weaknesses in it. +.Pp +The +.Nm +driver can be configured at runtime with +.Xr ifconfig 8 . +.Sh FILES +The driver needs at least version 1.0 of the following firmware files, +which are loaded when an interface is brought up: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Pa /boot/kernel/rtwn-rtl8192cfwU.ko +.It Pa /boot/kernel/rtwn-rtl8192cfwU_B.ko +.El +.Sh EXAMPLES +Join an existing BSS network (i.e., connect to an access point): +.Bd -literal -offset indent +ifconfig wlan create wlandev rtwn0 inet 192.168.0.20 \e + netmask 0xffffff00 +.Ed +.Pp +Join a specific BSS network with network name +.Dq Li my_net : +.Pp +.Dl "ifconfig wlan create wlandev rtwn0 ssid my_net up" +.Pp +Join a specific BSS network with 64-bit WEP encryption: +.Bd -literal -offset indent +ifconfig wlan create wlandev rtwn0 ssid my_net \e + wepmode on wepkey 0x1234567890 weptxkey 1 up +.Ed +.Sh DIAGNOSTICS +.Bl -diag +.It "could not read firmware %s" +For some reason, the driver was unable to read the microcode file from the +filesystem. +The file might be missing or corrupted. +.It "device timeout" +A frame dispatched to the hardware for transmission did not complete in time. +The driver will reset the hardware. +This should not happen. +.El +.Sh SEE ALSO +.Xr pci 4 , +.Xr rtwnfw 4 , +.Xr wlan 4 , +.Xr wlan_ccmp 4 , +.Xr wlan_tkip 4 , +.Xr wlan_wep 4 , +.Xr ifconfig 8 , +.Xr wpa_supplicant 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 5.8 . +.Sh AUTHORS +The +.Nm +driver was written by +.An -nosplit +.An Stefan Sperling Aq Mt stsp@openbsd.org +and ported by +.An Kevin Lo Aq Mt kevlo@freebsd.org . +It was based on the +.Xr urtwn 4 +driver written by +.An Damien Bergamini Aq Mt damien.bergamini@free.fr . +.Sh CAVEATS +The +.Nm +driver does not support any of the 802.11n capabilities offered by the +adapters. +Additional work is required in +.Xr ieee80211 9 +before those features can be supported. Property changes on: projects/clang380-import/share/man/man4/rtwn.4 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/share/man/man4/rtwnfw.4 =================================================================== --- projects/clang380-import/share/man/man4/rtwnfw.4 (nonexistent) +++ projects/clang380-import/share/man/man4/rtwnfw.4 (revision 293016) @@ -0,0 +1,74 @@ +.\" Copyright (c) 2015 Kevin Lo +.\" 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. 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 28, 2015 +.Dt RTWNFW 4 +.Os +.Sh NAME +.Nm rtwnfw +.Nd "Firmware Module for Realtek Wireless driver" +.Sh SYNOPSIS +To compile this module into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device rtwnfw" +.Ed +.Pp +This will include three firmware images inside the kernel. +If you want to pick only the firmware image for your network adapter choose one +of the following: +.Bd -ragged -offset indent +.Cd "device rtwn-rtl8192cfwU" +.Cd "device rtwn-rtl8192cfwU_B" +.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 +rtwn-rtl8192cfwU_load="YES" +rtwn-rtl8192cfwU_B_load="YES" +.Ed +.Sh DESCRIPTION +This module provides access to firmware sets for the +Realtek RTL8188CE chip based PCIe adapters. +It may be +statically linked into the kernel, or loaded as a module. +.Pp +For the loaded firmware to be enabled for use the license at +.Pa /usr/share/doc/legal/realtek.LICENSE +must be agreed to by adding the following line to +.Xr loader.conf 5 : +.Pp +.Dl "legal.realtek.license_ack=1" +.Sh FILES +.Bl -tag -width ".Pa /usr/share/doc/legal/realtek.LICENSE" -compact +.It Pa /usr/share/doc/legal/realtek.LICENSE +.Nm +firmware license +.El +.Sh SEE ALSO +.Xr rtwn 4 , +.Xr firmware 9 Property changes on: projects/clang380-import/share/man/man4/rtwnfw.4 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/share/man/man4 =================================================================== --- projects/clang380-import/share/man/man4 (revision 293015) +++ projects/clang380-import/share/man/man4 (revision 293016) Property changes on: projects/clang380-import/share/man/man4 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share/man/man4:r292913-293015 Index: projects/clang380-import/share =================================================================== --- projects/clang380-import/share (revision 293015) +++ projects/clang380-import/share (revision 293016) Property changes on: projects/clang380-import/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r292913-293015 Index: projects/clang380-import/sys/arm64/arm64/identcpu.c =================================================================== --- projects/clang380-import/sys/arm64/arm64/identcpu.c (revision 293015) +++ projects/clang380-import/sys/arm64/arm64/identcpu.c (revision 293016) @@ -1,189 +1,664 @@ /*- * Copyright (c) 2014 Andrew Turner * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Semihalf * under sponsorship of the FreeBSD Foundation. * * 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include +static int ident_lock; + char machine[] = "arm64"; SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, "Machine class"); /* * Per-CPU affinity as provided in MPIDR_EL1 * Indexed by CPU number in logical order selected by the system. * Relevant fields can be extracted using CPU_AFFn macros, * Aff3.Aff2.Aff1.Aff0 construct a unique CPU address in the system. * * Fields used by us: * Aff1 - Cluster number * Aff0 - CPU number in Aff1 cluster */ uint64_t __cpu_affinity[MAXCPU]; +static u_int cpu_aff_levels; struct cpu_desc { u_int cpu_impl; u_int cpu_part_num; u_int cpu_variant; u_int cpu_revision; const char *cpu_impl_name; const char *cpu_part_name; + + uint64_t mpidr; + uint64_t id_aa64afr0; + uint64_t id_aa64afr1; + uint64_t id_aa64dfr0; + uint64_t id_aa64dfr1; + uint64_t id_aa64isar0; + uint64_t id_aa64isar1; + uint64_t id_aa64mmfr0; + uint64_t id_aa64mmfr1; + uint64_t id_aa64pfr0; + uint64_t id_aa64pfr1; }; struct cpu_desc cpu_desc[MAXCPU]; +static u_int cpu_print_regs; +#define PRINT_ID_AA64_AFR0 0x00000001 +#define PRINT_ID_AA64_AFR1 0x00000002 +#define PRINT_ID_AA64_DFR0 0x00000004 +#define PRINT_ID_AA64_DFR1 0x00000008 +#define PRINT_ID_AA64_ISAR0 0x00000010 +#define PRINT_ID_AA64_ISAR1 0x00000020 +#define PRINT_ID_AA64_MMFR0 0x00000040 +#define PRINT_ID_AA64_MMFR1 0x00000080 +#define PRINT_ID_AA64_PFR0 0x00000100 +#define PRINT_ID_AA64_PFR1 0x00000200 struct cpu_parts { u_int part_id; const char *part_name; }; #define CPU_PART_NONE { 0, "Unknown Processor" } struct cpu_implementers { u_int impl_id; const char *impl_name; /* * Part number is implementation defined * so each vendor will have its own set of values and names. */ const struct cpu_parts *cpu_parts; }; #define CPU_IMPLEMENTER_NONE { 0, "Unknown Implementer", cpu_parts_none } /* * Per-implementer table of (PartNum, CPU Name) pairs. */ /* ARM Ltd. */ static const struct cpu_parts cpu_parts_arm[] = { { CPU_PART_FOUNDATION, "Foundation-Model" }, { CPU_PART_CORTEX_A53, "Cortex-A53" }, { CPU_PART_CORTEX_A57, "Cortex-A57" }, CPU_PART_NONE, }; /* Cavium */ static const struct cpu_parts cpu_parts_cavium[] = { { CPU_PART_THUNDER, "Thunder" }, CPU_PART_NONE, }; /* Unknown */ static const struct cpu_parts cpu_parts_none[] = { CPU_PART_NONE, }; /* * Implementers table. */ const struct cpu_implementers cpu_implementers[] = { { CPU_IMPL_ARM, "ARM", cpu_parts_arm }, { CPU_IMPL_BROADCOM, "Broadcom", cpu_parts_none }, { CPU_IMPL_CAVIUM, "Cavium", cpu_parts_cavium }, { CPU_IMPL_DEC, "DEC", cpu_parts_none }, { CPU_IMPL_INFINEON, "IFX", cpu_parts_none }, { CPU_IMPL_FREESCALE, "Freescale", cpu_parts_none }, { CPU_IMPL_NVIDIA, "NVIDIA", cpu_parts_none }, { CPU_IMPL_APM, "APM", cpu_parts_none }, { CPU_IMPL_QUALCOMM, "Qualcomm", cpu_parts_none }, { CPU_IMPL_MARVELL, "Marvell", cpu_parts_none }, { CPU_IMPL_INTEL, "Intel", cpu_parts_none }, CPU_IMPLEMENTER_NONE, }; -void identify_cpu(void); +void +print_cpu_features(u_int cpu) +{ + int printed; + printf("CPU%3d: %s %s r%dp%d", cpu, cpu_desc[cpu].cpu_impl_name, + cpu_desc[cpu].cpu_part_name, cpu_desc[cpu].cpu_variant, + cpu_desc[cpu].cpu_revision); + + printf(" affinity:"); + switch(cpu_aff_levels) { + default: + case 4: + printf(" %2d", CPU_AFF3(cpu_desc[cpu].mpidr)); + /* FALLTHROUGH */ + case 3: + printf(" %2d", CPU_AFF2(cpu_desc[cpu].mpidr)); + /* FALLTHROUGH */ + case 2: + printf(" %2d", CPU_AFF1(cpu_desc[cpu].mpidr)); + /* FALLTHROUGH */ + case 1: + case 0: /* On UP this will be zero */ + printf(" %2d", CPU_AFF0(cpu_desc[cpu].mpidr)); + break; + } + printf("\n"); + + if (cpu != 0 && cpu_print_regs == 0) + return; + +#define SEP_STR ((printed++) == 0) ? "" : "," + + /* AArch64 Instruction Set Attribute Register 0 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_ISAR0) != 0) { + printed = 0; + printf(" Instruction Set Attributes 0 = <"); + switch (ID_AA64ISAR0_AES(cpu_desc[cpu].id_aa64isar0)) { + case ID_AA64ISAR0_AES_NONE: + break; + case ID_AA64ISAR0_AES_BASE: + printf("%sAES", SEP_STR); + break; + case ID_AA64ISAR0_AES_PMULL: + printf("%sAES+PMULL", SEP_STR); + break; + default: + printf("%sUnknown AES", SEP_STR); + break; + } + + switch (ID_AA64ISAR0_SHA1(cpu_desc[cpu].id_aa64isar0)) { + case ID_AA64ISAR0_SHA1_NONE: + break; + case ID_AA64ISAR0_SHA1_BASE: + printf("%sSHA1", SEP_STR); + break; + default: + printf("%sUnknown SHA1", SEP_STR); + break; + } + + switch (ID_AA64ISAR0_SHA2(cpu_desc[cpu].id_aa64isar0)) { + case ID_AA64ISAR0_SHA2_NONE: + break; + case ID_AA64ISAR0_SHA2_BASE: + printf("%sSHA2", SEP_STR); + break; + default: + printf("%sUnknown SHA2", SEP_STR); + break; + } + + switch (ID_AA64ISAR0_CRC32(cpu_desc[cpu].id_aa64isar0)) { + case ID_AA64ISAR0_CRC32_NONE: + break; + case ID_AA64ISAR0_CRC32_BASE: + printf("%sCRC32", SEP_STR); + break; + default: + printf("%sUnknown CRC32", SEP_STR); + break; + } + + if ((cpu_desc[cpu].id_aa64isar0 & ~ID_AA64ISAR0_MASK) != 0) + printf("%s%#lx", SEP_STR, + cpu_desc[cpu].id_aa64isar0 & ~ID_AA64ISAR0_MASK); + + printf(">\n"); + } + + /* AArch64 Instruction Set Attribute Register 1 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_ISAR1) != 0) { + printf(" Instruction Set Attributes 1 = <%#lx>\n", + cpu_desc[cpu].id_aa64isar1); + } + + /* AArch64 Processor Feature Register 0 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_PFR0) != 0) { + printed = 0; + printf(" Processor Features 0 = <"); + switch (ID_AA64PFR0_GIC(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_GIC_CPUIF_NONE: + break; + case ID_AA64PFR0_GIC_CPUIF_EN: + printf("%sGIC", SEP_STR); + break; + default: + printf("%sUnknown GIC interface", SEP_STR); + break; + } + + switch (ID_AA64PFR0_ADV_SIMD(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_ADV_SIMD_NONE: + break; + case ID_AA64PFR0_ADV_SIMD_IMPL: + printf("%sAdvSIMD", SEP_STR); + break; + default: + printf("%sUnknown AdvSIMD", SEP_STR); + break; + } + + switch (ID_AA64PFR0_FP(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_FP_NONE: + break; + case ID_AA64PFR0_FP_IMPL: + printf("%sFloat", SEP_STR); + break; + default: + printf("%sUnknown Float", SEP_STR); + break; + } + + switch (ID_AA64PFR0_EL3(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_EL3_NONE: + printf("%sNo EL3", SEP_STR); + break; + case ID_AA64PFR0_EL3_64: + printf("%sEL3", SEP_STR); + break; + case ID_AA64PFR0_EL3_64_32: + printf("%sEL3 32", SEP_STR); + break; + default: + printf("%sUnknown EL3", SEP_STR); + break; + } + + switch (ID_AA64PFR0_EL2(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_EL2_NONE: + printf("%sNo EL2", SEP_STR); + break; + case ID_AA64PFR0_EL2_64: + printf("%sEL2", SEP_STR); + break; + case ID_AA64PFR0_EL2_64_32: + printf("%sEL2 32", SEP_STR); + break; + default: + printf("%sUnknown EL2", SEP_STR); + break; + } + + switch (ID_AA64PFR0_EL1(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_EL1_64: + printf("%sEL1", SEP_STR); + break; + case ID_AA64PFR0_EL1_64_32: + printf("%sEL1 32", SEP_STR); + break; + default: + printf("%sUnknown EL1", SEP_STR); + break; + } + + switch (ID_AA64PFR0_EL0(cpu_desc[cpu].id_aa64pfr0)) { + case ID_AA64PFR0_EL0_64: + printf("%sEL0", SEP_STR); + break; + case ID_AA64PFR0_EL0_64_32: + printf("%sEL0 32", SEP_STR); + break; + default: + printf("%sUnknown EL0", SEP_STR); + break; + } + + if ((cpu_desc[cpu].id_aa64pfr0 & ~ID_AA64PFR0_MASK) != 0) + printf("%s%#lx", SEP_STR, + cpu_desc[cpu].id_aa64pfr0 & ~ID_AA64PFR0_MASK); + + printf(">\n"); + } + + /* AArch64 Processor Feature Register 1 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_PFR1) != 0) { + printf(" Processor Features 1 = <%#lx>\n", + cpu_desc[cpu].id_aa64pfr1); + } + + /* AArch64 Memory Model Feature Register 0 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_MMFR0) != 0) { + printed = 0; + printf(" Memory Model Features 0 = <"); + switch (ID_AA64MMFR0_TGRAN4(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_TGRAN4_NONE: + break; + case ID_AA64MMFR0_TGRAN4_IMPL: + printf("%s4k Granule", SEP_STR); + break; + default: + printf("%sUnknown 4k Granule", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_TGRAN16(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_TGRAN16_NONE: + break; + case ID_AA64MMFR0_TGRAN16_IMPL: + printf("%s16k Granule", SEP_STR); + break; + default: + printf("%sUnknown 16k Granule", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_TGRAN64(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_TGRAN64_NONE: + break; + case ID_AA64MMFR0_TGRAN64_IMPL: + printf("%s64k Granule", SEP_STR); + break; + default: + printf("%sUnknown 64k Granule", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_BIGEND(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_BIGEND_FIXED: + break; + case ID_AA64MMFR0_BIGEND_MIXED: + printf("%sMixedEndian", SEP_STR); + break; + default: + printf("%sUnknown Endian switching", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_BIGEND_EL0(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_BIGEND_EL0_FIXED: + break; + case ID_AA64MMFR0_BIGEND_EL0_MIXED: + printf("%sEL0 MixEndian", SEP_STR); + break; + default: + printf("%sUnknown EL0 Endian switching", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_S_NS_MEM(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_S_NS_MEM_NONE: + break; + case ID_AA64MMFR0_S_NS_MEM_DISTINCT: + printf("%sS/NS Mem", SEP_STR); + break; + default: + printf("%sUnknown S/NS Mem", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_ASID_BITS(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_ASID_BITS_8: + printf("%s8bit ASID", SEP_STR); + break; + case ID_AA64MMFR0_ASID_BITS_16: + printf("%s16bit ASID", SEP_STR); + break; + default: + printf("%sUnknown ASID", SEP_STR); + break; + } + + switch (ID_AA64MMFR0_PA_RANGE(cpu_desc[cpu].id_aa64mmfr0)) { + case ID_AA64MMFR0_PA_RANGE_4G: + printf("%s4GB PA", SEP_STR); + break; + case ID_AA64MMFR0_PA_RANGE_64G: + printf("%s64GB PA", SEP_STR); + break; + case ID_AA64MMFR0_PA_RANGE_1T: + printf("%s1TB PA", SEP_STR); + break; + case ID_AA64MMFR0_PA_RANGE_4T: + printf("%s4TB PA", SEP_STR); + break; + case ID_AA64MMFR0_PA_RANGE_16T: + printf("%s16TB PA", SEP_STR); + break; + case ID_AA64MMFR0_PA_RANGE_256T: + printf("%s256TB PA", SEP_STR); + break; + default: + printf("%sUnknown PA Range", SEP_STR); + break; + } + + if ((cpu_desc[cpu].id_aa64mmfr0 & ~ID_AA64MMFR0_MASK) != 0) + printf("%s%#lx", SEP_STR, + cpu_desc[cpu].id_aa64mmfr0 & ~ID_AA64MMFR0_MASK); + printf(">\n"); + } + + /* AArch64 Memory Model Feature Register 1 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_MMFR1) != 0) { + printf(" Memory Model Features 1 = <%#lx>\n", + cpu_desc[cpu].id_aa64mmfr1); + } + + /* AArch64 Debug Feature Register 0 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_DFR0) != 0) { + printed = 0; + printf(" Debug Features 0 = <"); + printf("%s%lu CTX Breakpoints", SEP_STR, + ID_AA64DFR0_CTX_CMPS(cpu_desc[cpu].id_aa64dfr0)); + + printf("%s%lu Watchpoints", SEP_STR, + ID_AA64DFR0_WRPS(cpu_desc[cpu].id_aa64dfr0)); + + printf("%s%lu Breakpoints", SEP_STR, + ID_AA64DFR0_BRPS(cpu_desc[cpu].id_aa64dfr0)); + + switch (ID_AA64DFR0_PMU_VER(cpu_desc[cpu].id_aa64dfr0)) { + case ID_AA64DFR0_PMU_VER_NONE: + break; + case ID_AA64DFR0_PMU_VER_3: + printf("%sPMUv3", SEP_STR); + break; + case ID_AA64DFR0_PMU_VER_IMPL: + printf("%sImplementation defined PMU", SEP_STR); + break; + default: + printf("%sUnknown PMU", SEP_STR); + break; + } + + switch (ID_AA64DFR0_TRACE_VER(cpu_desc[cpu].id_aa64dfr0)) { + case ID_AA64DFR0_TRACE_VER_NONE: + break; + case ID_AA64DFR0_TRACE_VER_IMPL: + printf("%sTrace", SEP_STR); + break; + default: + printf("%sUnknown Trace", SEP_STR); + break; + } + + switch (ID_AA64DFR0_DEBUG_VER(cpu_desc[cpu].id_aa64dfr0)) { + case ID_AA64DFR0_DEBUG_VER_8: + printf("%sDebug v8", SEP_STR); + break; + default: + printf("%sUnknown Debug", SEP_STR); + break; + } + + if (cpu_desc[cpu].id_aa64dfr0 & ~ID_AA64DFR0_MASK) + printf("%s%#lx", SEP_STR, + cpu_desc[cpu].id_aa64dfr0 & ~ID_AA64DFR0_MASK); + printf(">\n"); + } + + /* AArch64 Memory Model Feature Register 1 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_DFR1) != 0) { + printf(" Debug Features 1 = <%#lx>\n", + cpu_desc[cpu].id_aa64dfr1); + } + + /* AArch64 Auxiliary Feature Register 0 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_AFR0) != 0) { + printf(" Auxiliary Features 0 = <%#lx>\n", + cpu_desc[cpu].id_aa64afr0); + } + + /* AArch64 Auxiliary Feature Register 1 */ + if (cpu == 0 || (cpu_print_regs & PRINT_ID_AA64_AFR1) != 0) { + printf(" Auxiliary Features 1 = <%#lx>\n", + cpu_desc[cpu].id_aa64afr1); + } + +#undef SEP_STR +} + void identify_cpu(void) { u_int midr; u_int impl_id; u_int part_id; u_int cpu; - uint64_t mpidr; size_t i; const struct cpu_parts *cpu_partsp = NULL; cpu = PCPU_GET(cpuid); midr = get_midr(); /* * Store midr to pcpu to allow fast reading * from EL0, EL1 and assembly code. */ PCPU_SET(midr, midr); impl_id = CPU_IMPL(midr); for (i = 0; i < nitems(cpu_implementers); i++) { if (impl_id == cpu_implementers[i].impl_id || cpu_implementers[i].impl_id == 0) { cpu_desc[cpu].cpu_impl = impl_id; cpu_desc[cpu].cpu_impl_name = cpu_implementers[i].impl_name; cpu_partsp = cpu_implementers[i].cpu_parts; break; } } part_id = CPU_PART(midr); for (i = 0; &cpu_partsp[i] != NULL; i++) { if (part_id == cpu_partsp[i].part_id || cpu_partsp[i].part_id == 0) { cpu_desc[cpu].cpu_part_num = part_id; cpu_desc[cpu].cpu_part_name = cpu_partsp[i].part_name; break; } } cpu_desc[cpu].cpu_revision = CPU_REV(midr); cpu_desc[cpu].cpu_variant = CPU_VAR(midr); /* Save affinity for current CPU */ - mpidr = get_mpidr(); - CPU_AFFINITY(cpu) = mpidr & CPU_AFF_MASK; + cpu_desc[cpu].mpidr = get_mpidr(); + CPU_AFFINITY(cpu) = cpu_desc[cpu].mpidr & CPU_AFF_MASK; - /* Print details for boot CPU or if we want verbose output */ - if (cpu == 0 || bootverbose) { - printf("CPU(%d): %s %s r%dp%d\n", cpu, - cpu_desc[cpu].cpu_impl_name, - cpu_desc[cpu].cpu_part_name, - cpu_desc[cpu].cpu_variant, - cpu_desc[cpu].cpu_revision); - } + cpu_desc[cpu].id_aa64dfr0 = READ_SPECIALREG(id_aa64dfr0_el1); + cpu_desc[cpu].id_aa64dfr1 = READ_SPECIALREG(id_aa64dfr1_el1); + cpu_desc[cpu].id_aa64isar0 = READ_SPECIALREG(id_aa64isar0_el1); + cpu_desc[cpu].id_aa64isar1 = READ_SPECIALREG(id_aa64isar1_el1); + cpu_desc[cpu].id_aa64mmfr0 = READ_SPECIALREG(id_aa64mmfr0_el1); + cpu_desc[cpu].id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1); + cpu_desc[cpu].id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1); + cpu_desc[cpu].id_aa64pfr1 = READ_SPECIALREG(id_aa64pfr1_el1); - if (bootverbose) - printf("CPU%u affinity: %u.%u.%u.%u\n", 0, CPU_AFF0(mpidr), - CPU_AFF1(mpidr), CPU_AFF2(mpidr), CPU_AFF3(mpidr)); + if (cpu != 0) { + /* + * This code must run on one cpu at a time, but we are + * not scheduling on the current core so implement a + * simple spinlock. + */ + while (atomic_cmpset_acq_int(&ident_lock, 0, 1) == 0) + __asm __volatile("wfe" ::: "memory"); + + switch (cpu_aff_levels) { + case 0: + if (CPU_AFF0(cpu_desc[cpu].mpidr) != + CPU_AFF0(cpu_desc[0].mpidr)) + cpu_aff_levels = 1; + /* FALLTHROUGH */ + case 1: + if (CPU_AFF1(cpu_desc[cpu].mpidr) != + CPU_AFF1(cpu_desc[0].mpidr)) + cpu_aff_levels = 2; + /* FALLTHROUGH */ + case 2: + if (CPU_AFF2(cpu_desc[cpu].mpidr) != + CPU_AFF2(cpu_desc[0].mpidr)) + cpu_aff_levels = 3; + /* FALLTHROUGH */ + case 3: + if (CPU_AFF3(cpu_desc[cpu].mpidr) != + CPU_AFF3(cpu_desc[0].mpidr)) + cpu_aff_levels = 4; + break; + } + + if (cpu_desc[cpu].id_aa64afr0 != cpu_desc[0].id_aa64afr0) + cpu_print_regs |= PRINT_ID_AA64_AFR0; + if (cpu_desc[cpu].id_aa64afr1 != cpu_desc[0].id_aa64afr1) + cpu_print_regs |= PRINT_ID_AA64_AFR1; + + if (cpu_desc[cpu].id_aa64dfr0 != cpu_desc[0].id_aa64dfr0) + cpu_print_regs |= PRINT_ID_AA64_DFR0; + if (cpu_desc[cpu].id_aa64dfr1 != cpu_desc[0].id_aa64dfr1) + cpu_print_regs |= PRINT_ID_AA64_DFR1; + + if (cpu_desc[cpu].id_aa64isar0 != cpu_desc[0].id_aa64isar0) + cpu_print_regs |= PRINT_ID_AA64_ISAR0; + if (cpu_desc[cpu].id_aa64isar1 != cpu_desc[0].id_aa64isar1) + cpu_print_regs |= PRINT_ID_AA64_ISAR1; + + if (cpu_desc[cpu].id_aa64mmfr0 != cpu_desc[0].id_aa64mmfr0) + cpu_print_regs |= PRINT_ID_AA64_MMFR0; + if (cpu_desc[cpu].id_aa64mmfr1 != cpu_desc[0].id_aa64mmfr1) + cpu_print_regs |= PRINT_ID_AA64_MMFR1; + + if (cpu_desc[cpu].id_aa64pfr0 != cpu_desc[0].id_aa64pfr0) + cpu_print_regs |= PRINT_ID_AA64_PFR0; + if (cpu_desc[cpu].id_aa64pfr1 != cpu_desc[0].id_aa64pfr1) + cpu_print_regs |= PRINT_ID_AA64_PFR1; + + /* Wake up the other CPUs */ + atomic_store_rel_int(&ident_lock, 0); + __asm __volatile("sev" ::: "memory"); + } } Index: projects/clang380-import/sys/arm64/arm64/mp_machdep.c =================================================================== --- projects/clang380-import/sys/arm64/arm64/mp_machdep.c (revision 293015) +++ projects/clang380-import/sys/arm64/arm64/mp_machdep.c (revision 293016) @@ -1,440 +1,446 @@ /*- * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * 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. * */ #include "opt_kstack_pages.h" #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef VFP #include #endif #ifdef FDT #include #include #endif #include boolean_t ofw_cpu_reg(phandle_t node, u_int, cell_t *); extern struct pcpu __pcpu[]; static enum { CPUS_UNKNOWN, #ifdef FDT CPUS_FDT, #endif } cpu_enum_method; static device_identify_t arm64_cpu_identify; static device_probe_t arm64_cpu_probe; static device_attach_t arm64_cpu_attach; static int ipi_handler(void *arg); struct mtx ap_boot_mtx; struct pcb stoppcbs[MAXCPU]; #ifdef INVARIANTS static uint32_t cpu_reg[MAXCPU][2]; #endif static device_t cpu_list[MAXCPU]; void mpentry(unsigned long cpuid); void init_secondary(uint64_t); uint8_t secondary_stacks[MAXCPU - 1][PAGE_SIZE * KSTACK_PAGES] __aligned(16); /* Set to 1 once we're ready to let the APs out of the pen. */ volatile int aps_ready = 0; /* Temporary variables for init_secondary() */ void *dpcpu[MAXCPU - 1]; static device_method_t arm64_cpu_methods[] = { /* Device interface */ DEVMETHOD(device_identify, arm64_cpu_identify), DEVMETHOD(device_probe, arm64_cpu_probe), DEVMETHOD(device_attach, arm64_cpu_attach), DEVMETHOD_END }; static devclass_t arm64_cpu_devclass; static driver_t arm64_cpu_driver = { "arm64_cpu", arm64_cpu_methods, 0 }; DRIVER_MODULE(arm64_cpu, cpu, arm64_cpu_driver, arm64_cpu_devclass, 0, 0); static void arm64_cpu_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "arm64_cpu", -1) != NULL) return; if (BUS_ADD_CHILD(parent, 0, "arm64_cpu", -1) == NULL) device_printf(parent, "add child failed\n"); } static int arm64_cpu_probe(device_t dev) { u_int cpuid; cpuid = device_get_unit(dev); if (cpuid >= MAXCPU || cpuid > mp_maxid) return (EINVAL); device_quiet(dev); return (0); } static int arm64_cpu_attach(device_t dev) { const uint32_t *reg; size_t reg_size; u_int cpuid; int i; cpuid = device_get_unit(dev); if (cpuid >= MAXCPU || cpuid > mp_maxid) return (EINVAL); KASSERT(cpu_list[cpuid] == NULL, ("Already have cpu %u", cpuid)); reg = cpu_get_cpuid(dev, ®_size); if (reg == NULL) return (EINVAL); if (bootverbose) { device_printf(dev, "register <"); for (i = 0; i < reg_size; i++) printf("%s%x", (i == 0) ? "" : " ", reg[i]); printf(">\n"); } /* Set the device to start it later */ cpu_list[cpuid] = dev; return (0); } static void release_aps(void *dummy __unused) { - int i; + int cpu, i; /* Setup the IPI handler */ for (i = 0; i < COUNT_IPI; i++) arm_setup_ipihandler(ipi_handler, i); atomic_store_rel_int(&aps_ready, 1); /* Wake up the other CPUs */ __asm __volatile("sev"); printf("Release APs\n"); for (i = 0; i < 2000; i++) { - if (smp_started) + if (smp_started) { + for (cpu = 0; cpu <= mp_maxid; cpu++) { + if (CPU_ABSENT(cpu)) + continue; + print_cpu_features(cpu); + } return; + } DELAY(1000); } printf("APs not started\n"); } SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL); void init_secondary(uint64_t cpu) { struct pcpu *pcpup; int i; pcpup = &__pcpu[cpu]; /* * Set the pcpu pointer with a backup in tpidr_el1 to be * loaded when entering the kernel from userland. */ __asm __volatile( "mov x18, %0 \n" "msr tpidr_el1, %0" :: "r"(pcpup)); /* Spin until the BSP releases the APs */ while (!aps_ready) __asm __volatile("wfe"); /* Initialize curthread */ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread")); pcpup->pc_curthread = pcpup->pc_idlethread; pcpup->pc_curpcb = pcpup->pc_idlethread->td_pcb; /* * Identify current CPU. This is necessary to setup * affinity registers and to provide support for * runtime chip identification. */ identify_cpu(); /* Configure the interrupt controller */ arm_init_secondary(); for (i = 0; i < COUNT_IPI; i++) arm_unmask_ipi(i); /* Start per-CPU event timers. */ cpu_initclocks_ap(); #ifdef VFP vfp_init(); #endif /* Enable interrupts */ intr_enable(); mtx_lock_spin(&ap_boot_mtx); atomic_add_rel_32(&smp_cpus, 1); if (smp_cpus == mp_ncpus) { /* enable IPI's, tlb shootdown, freezes etc */ atomic_store_rel_int(&smp_started, 1); } mtx_unlock_spin(&ap_boot_mtx); /* Enter the scheduler */ sched_throw(NULL); panic("scheduler returned us to init_secondary"); /* NOTREACHED */ } static int ipi_handler(void *arg) { u_int cpu, ipi; arg = (void *)((uintptr_t)arg & ~(1 << 16)); KASSERT((uintptr_t)arg < COUNT_IPI, ("Invalid IPI %ju", (uintptr_t)arg)); cpu = PCPU_GET(cpuid); ipi = (uintptr_t)arg; switch(ipi) { case IPI_AST: CTR0(KTR_SMP, "IPI_AST"); break; case IPI_PREEMPT: CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); sched_preempt(curthread); break; case IPI_RENDEZVOUS: CTR0(KTR_SMP, "IPI_RENDEZVOUS"); smp_rendezvous_action(); break; case IPI_STOP: case IPI_STOP_HARD: CTR0(KTR_SMP, (ipi == IPI_STOP) ? "IPI_STOP" : "IPI_STOP_HARD"); savectx(&stoppcbs[cpu]); /* Indicate we are stopped */ CPU_SET_ATOMIC(cpu, &stopped_cpus); /* Wait for restart */ while (!CPU_ISSET(cpu, &started_cpus)) cpu_spinwait(); CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); CTR0(KTR_SMP, "IPI_STOP (restart)"); break; case IPI_HARDCLOCK: CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); hardclockintr(); break; default: panic("Unknown IPI %#0x on cpu %d", ipi, curcpu); } return (FILTER_HANDLED); } struct cpu_group * cpu_topo(void) { return (smp_topo_none()); } /* Determine if we running MP machine */ int cpu_mp_probe(void) { /* ARM64TODO: Read the u bit of mpidr_el1 to determine this */ return (1); } #ifdef FDT static boolean_t cpu_init_fdt(u_int id, phandle_t node, u_int addr_size, pcell_t *reg) { uint64_t target_cpu; struct pcpu *pcpup; vm_paddr_t pa; int err; /* Check we are able to start this cpu */ if (id > mp_maxid) return (0); KASSERT(id < MAXCPU, ("Too mant CPUs")); KASSERT(addr_size == 1 || addr_size == 2, ("Invalid register size")); #ifdef INVARIANTS cpu_reg[id][0] = reg[0]; if (addr_size == 2) cpu_reg[id][1] = reg[1]; #endif /* We are already running on cpu 0 */ if (id == 0) return (1); pcpup = &__pcpu[id]; pcpu_init(pcpup, id, sizeof(struct pcpu)); dpcpu[id - 1] = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, M_WAITOK | M_ZERO); dpcpu_init(dpcpu[id - 1], id); target_cpu = reg[0]; if (addr_size == 2) { target_cpu <<= 32; target_cpu |= reg[1]; } printf("Starting CPU %u (%lx)\n", id, target_cpu); pa = pmap_extract(kernel_pmap, (vm_offset_t)mpentry); err = psci_cpu_on(target_cpu, pa, id); if (err != PSCI_RETVAL_SUCCESS) { /* Panic here if INVARIANTS are enabled */ KASSERT(0, ("Failed to start CPU %u (%lx)\n", id, target_cpu)); pcpu_destroy(pcpup); kmem_free(kernel_arena, (vm_offset_t)dpcpu[id - 1], DPCPU_SIZE); dpcpu[id - 1] = NULL; /* Notify the user that the CPU failed to start */ printf("Failed to start CPU %u (%lx)\n", id, target_cpu); } else CPU_SET(id, &all_cpus); return (1); } #endif /* Initialize and fire up non-boot processors */ void cpu_mp_start(void) { mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); CPU_SET(0, &all_cpus); switch(cpu_enum_method) { #ifdef FDT case CPUS_FDT: ofw_cpu_early_foreach(cpu_init_fdt, true); break; #endif case CPUS_UNKNOWN: break; } } /* Introduce rest of cores to the world */ void cpu_mp_announce(void) { } void cpu_mp_setmaxid(void) { #ifdef FDT int cores; cores = ofw_cpu_early_foreach(NULL, false); if (cores > 0) { cores = MIN(cores, MAXCPU); if (bootverbose) printf("Found %d CPUs in the device tree\n", cores); mp_ncpus = cores; mp_maxid = cores - 1; cpu_enum_method = CPUS_FDT; return; } #endif if (bootverbose) printf("No CPU data, limiting to 1 core\n"); mp_ncpus = 1; mp_maxid = 0; } Index: projects/clang380-import/sys/arm64/include/armreg.h =================================================================== --- projects/clang380-import/sys/arm64/include/armreg.h (revision 293015) +++ projects/clang380-import/sys/arm64/include/armreg.h (revision 293016) @@ -1,264 +1,392 @@ /*- * Copyright (c) 2013, 2014 Andrew Turner * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * 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$ */ #ifndef _MACHINE_ARMREG_H_ #define _MACHINE_ARMREG_H_ #define READ_SPECIALREG(reg) \ ({ uint64_t val; \ __asm __volatile("mrs %0, " __STRING(reg) : "=&r" (val)); \ val; \ }) #define WRITE_SPECIALREG(reg, val) \ __asm __volatile("msr " __STRING(reg) ", %0" : : "r"((uint64_t)val)) /* CNTHCTL_EL2 - Counter-timer Hypervisor Control register */ #define CNTHCTL_EVNTI_MASK (0xf << 4) /* Bit to trigger event stream */ #define CNTHCTL_EVNTDIR (1 << 3) /* Control transition trigger bit */ #define CNTHCTL_EVNTEN (1 << 2) /* Enable event stream */ #define CNTHCTL_EL1PCEN (1 << 1) /* Allow EL0/1 physical timer access */ #define CNTHCTL_EL1PCTEN (1 << 0) /*Allow EL0/1 physical counter access*/ /* CPACR_EL1 */ #define CPACR_FPEN_MASK (0x3 << 20) #define CPACR_FPEN_TRAP_ALL1 (0x0 << 20) /* Traps from EL0 and EL1 */ #define CPACR_FPEN_TRAP_EL0 (0x1 << 20) /* Traps from EL0 */ #define CPACR_FPEN_TRAP_ALL2 (0x2 << 20) /* Traps from EL0 and EL1 */ #define CPACR_FPEN_TRAP_NONE (0x3 << 20) /* No traps */ #define CPACR_TTA (0x1 << 28) /* CTR_EL0 - Cache Type Register */ #define CTR_DLINE_SHIFT 16 #define CTR_DLINE_MASK (0xf << CTR_DLINE_SHIFT) #define CTR_DLINE_SIZE(reg) (((reg) & CTR_DLINE_MASK) >> CTR_DLINE_SHIFT) #define CTR_ILINE_SHIFT 0 #define CTR_ILINE_MASK (0xf << CTR_ILINE_SHIFT) #define CTR_ILINE_SIZE(reg) (((reg) & CTR_ILINE_MASK) >> CTR_ILINE_SHIFT) /* ESR_ELx */ #define ESR_ELx_ISS_MASK 0x00ffffff #define ISS_INSN_FnV (0x01 << 10) #define ISS_INSN_EA (0x01 << 9) #define ISS_INSN_S1PTW (0x01 << 7) #define ISS_INSN_IFSC_MASK (0x1f << 0) #define ISS_DATA_ISV (0x01 << 24) #define ISS_DATA_SAS_MASK (0x03 << 22) #define ISS_DATA_SSE (0x01 << 21) #define ISS_DATA_SRT_MASK (0x1f << 16) #define ISS_DATA_SF (0x01 << 15) #define ISS_DATA_AR (0x01 << 14) #define ISS_DATA_FnV (0x01 << 10) #define ISS_DATa_EA (0x01 << 9) #define ISS_DATa_CM (0x01 << 8) #define ISS_INSN_S1PTW (0x01 << 7) #define ISS_DATa_WnR (0x01 << 6) #define ISS_DATA_DFSC_MASK (0x1f << 0) #define ESR_ELx_IL (0x01 << 25) #define ESR_ELx_EC_SHIFT 26 #define ESR_ELx_EC_MASK (0x3f << 26) #define ESR_ELx_EXCEPTION(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT) #define EXCP_UNKNOWN 0x00 /* Unkwn exception */ #define EXCP_FP_SIMD 0x07 /* VFP/SIMD trap */ #define EXCP_ILL_STATE 0x0e /* Illegal execution state */ #define EXCP_SVC 0x15 /* SVC trap */ #define EXCP_MSR 0x18 /* MSR/MRS trap */ #define EXCP_INSN_ABORT_L 0x20 /* Instruction abort, from lower EL */ #define EXCP_INSN_ABORT 0x21 /* Instruction abort, from same EL */ #define EXCP_PC_ALIGN 0x22 /* PC alignment fault */ #define EXCP_DATA_ABORT_L 0x24 /* Data abort, from lower EL */ #define EXCP_DATA_ABORT 0x25 /* Data abort, from same EL */ #define EXCP_SP_ALIGN 0x26 /* SP slignment fault */ #define EXCP_TRAP_FP 0x2c /* Trapped FP exception */ #define EXCP_SERROR 0x2f /* SError interrupt */ #define EXCP_SOFTSTP_EL1 0x33 /* Software Step, from same EL */ #define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */ #define EXCP_BRK 0x3c /* Breakpoint */ /* ICC_CTLR_EL1 */ #define ICC_CTLR_EL1_EOIMODE (1U << 1) /* ICC_IAR1_EL1 */ #define ICC_IAR1_EL1_SPUR (0x03ff) /* ICC_IGRPEN0_EL1 */ #define ICC_IGRPEN0_EL1_EN (1U << 0) /* ICC_PMR_EL1 */ #define ICC_PMR_EL1_PRIO_MASK (0xFFUL) /* ICC_SRE_EL1 */ #define ICC_SRE_EL1_SRE (1U << 0) /* ICC_SRE_EL2 */ #define ICC_SRE_EL2_EN (1U << 3) +/* ID_AA64DFR0_EL1 */ +#define ID_AA64DFR0_MASK 0xf0f0ffff +#define ID_AA64DFR0_DEBUG_VER_SHIFT 0 +#define ID_AA64DFR0_DEBUG_VER_MASK (0xf << ID_AA64DFR0_DEBUG_VER_SHIFT) +#define ID_AA64DFR0_DEBUG_VER(x) ((x) & ID_AA64DFR0_DEBUG_VER_MASK) +#define ID_AA64DFR0_DEBUG_VER_8 (0x6 << ID_AA64DFR0_DEBUG_VER_SHIFT) +#define ID_AA64DFR0_TRACE_VER_SHIFT 4 +#define ID_AA64DFR0_TRACE_VER_MASK (0xf << ID_AA64DFR0_TRACE_VER_SHIFT) +#define ID_AA64DFR0_TRACE_VER(x) ((x) & ID_AA64DFR0_TRACE_VER_MASK) +#define ID_AA64DFR0_TRACE_VER_NONE (0x0 << ID_AA64DFR0_TRACE_VER_SHIFT) +#define ID_AA64DFR0_TRACE_VER_IMPL (0x1 << ID_AA64DFR0_TRACE_VER_SHIFT) +#define ID_AA64DFR0_PMU_VER_SHIFT 8 +#define ID_AA64DFR0_PMU_VER_MASK (0xf << ID_AA64DFR0_PMU_VER_SHIFT) +#define ID_AA64DFR0_PMU_VER(x) ((x) & ID_AA64DFR0_PMU_VER_MASK) +#define ID_AA64DFR0_PMU_VER_NONE (0x0 << ID_AA64DFR0_PMU_VER_SHIFT) +#define ID_AA64DFR0_PMU_VER_3 (0x1 << ID_AA64DFR0_PMU_VER_SHIFT) +#define ID_AA64DFR0_PMU_VER_IMPL (0xf << ID_AA64DFR0_PMU_VER_SHIFT) +#define ID_AA64DFR0_BRPS_SHIFT 12 +#define ID_AA64DFR0_BRPS_MASK (0xf << ID_AA64DFR0_BRPS_SHIFT) +#define ID_AA64DFR0_BRPS(x) \ + ((((x) >> ID_AA64DFR0_BRPS_SHIFT) & 0xf) + 1) +#define ID_AA64DFR0_WRPS_SHIFT 20 +#define ID_AA64DFR0_WRPS_MASK (0xf << ID_AA64DFR0_WRPS_SHIFT) +#define ID_AA64DFR0_WRPS(x) \ + ((((x) >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) + 1) +#define ID_AA64DFR0_CTX_CMPS_SHIFT 28 +#define ID_AA64DFR0_CTX_CMPS_MASK (0xf << ID_AA64DFR0_CTX_CMPS_SHIFT) +#define ID_AA64DFR0_CTX_CMPS(x) \ + ((((x) >> ID_AA64DFR0_CTX_CMPS_SHIFT) & 0xf) + 1) + +/* ID_AA64ISAR0_EL1 */ +#define ID_AA64ISAR0_MASK 0x000ffff0 +#define ID_AA64ISAR0_AES_SHIFT 4 +#define ID_AA64ISAR0_AES_MASK (0xf << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES(x) ((x) & ID_AA64ISAR0_AES_MASK) +#define ID_AA64ISAR0_AES_NONE (0x0 << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES_BASE (0x1 << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_AES_PMULL (0x2 << ID_AA64ISAR0_AES_SHIFT) +#define ID_AA64ISAR0_SHA1_SHIFT 8 +#define ID_AA64ISAR0_SHA1_MASK (0xf << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA1(x) ((x) & ID_AA64ISAR0_SHA1_MASK) +#define ID_AA64ISAR0_SHA1_NONE (0x0 << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA1_BASE (0x1 << ID_AA64ISAR0_SHA1_SHIFT) +#define ID_AA64ISAR0_SHA2_SHIFT 12 +#define ID_AA64ISAR0_SHA2_MASK (0xf << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_SHA2(x) ((x) & ID_AA64ISAR0_SHA2_MASK) +#define ID_AA64ISAR0_SHA2_NONE (0x0 << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_SHA2_BASE (0x1 << ID_AA64ISAR0_SHA2_SHIFT) +#define ID_AA64ISAR0_CRC32_SHIFT 16 +#define ID_AA64ISAR0_CRC32_MASK (0xf << ID_AA64ISAR0_CRC32_SHIFT) +#define ID_AA64ISAR0_CRC32(x) ((x) & ID_AA64ISAR0_CRC32_MASK) +#define ID_AA64ISAR0_CRC32_NONE (0x0 << ID_AA64ISAR0_CRC32_SHIFT) +#define ID_AA64ISAR0_CRC32_BASE (0x1 << ID_AA64ISAR0_CRC32_SHIFT) + +/* ID_AA64MMFR0_EL1 */ +#define ID_AA64MMFR0_MASK 0xffffffff +#define ID_AA64MMFR0_PA_RANGE_SHIFT 0 +#define ID_AA64MMFR0_PA_RANGE_MASK (0xf << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE(x) ((x) & ID_AA64MMFR0_PA_RANGE_MASK) +#define ID_AA64MMFR0_PA_RANGE_4G (0x0 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE_64G (0x1 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE_1T (0x2 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE_4T (0x3 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE_16T (0x4 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_PA_RANGE_256T (0x5 << ID_AA64MMFR0_PA_RANGE_SHIFT) +#define ID_AA64MMFR0_ASID_BITS_SHIFT 4 +#define ID_AA64MMFR0_ASID_BITS_MASK (0xf << ID_AA64MMFR0_ASID_BITS_SHIFT) +#define ID_AA64MMFR0_ASID_BITS(x) ((x) & ID_AA64MMFR0_ASID_BITS_MASK) +#define ID_AA64MMFR0_ASID_BITS_8 (0x0 << ID_AA64MMFR0_ASID_BITS_SHIFT) +#define ID_AA64MMFR0_ASID_BITS_16 (0x2 << ID_AA64MMFR0_ASID_BITS_SHIFT) +#define ID_AA64MMFR0_BIGEND_SHIFT 8 +#define ID_AA64MMFR0_BIGEND_MASK (0xf << ID_AA64MMFR0_BIGEND_SHIFT) +#define ID_AA64MMFR0_BIGEND(x) ((x) & ID_AA64MMFR0_BIGEND_MASK) +#define ID_AA64MMFR0_BIGEND_FIXED (0x0 << ID_AA64MMFR0_BIGEND_SHIFT) +#define ID_AA64MMFR0_BIGEND_MIXED (0x1 << ID_AA64MMFR0_BIGEND_SHIFT) +#define ID_AA64MMFR0_S_NS_MEM_SHIFT 12 +#define ID_AA64MMFR0_S_NS_MEM_MASK (0xf << ID_AA64MMFR0_S_NS_MEM_SHIFT) +#define ID_AA64MMFR0_S_NS_MEM(x) ((x) & ID_AA64MMFR0_S_NS_MEM_MASK) +#define ID_AA64MMFR0_S_NS_MEM_NONE (0x0 << ID_AA64MMFR0_S_NS_MEM_SHIFT) +#define ID_AA64MMFR0_S_NS_MEM_DISTINCT (0x1 << ID_AA64MMFR0_S_NS_MEM_SHIFT) +#define ID_AA64MMFR0_BIGEND_EL0_SHIFT 16 +#define ID_AA64MMFR0_BIGEND_EL0_MASK (0xf << ID_AA64MMFR0_BIGEND_EL0_SHIFT) +#define ID_AA64MMFR0_BIGEND_EL0(x) ((x) & ID_AA64MMFR0_BIGEND_EL0_MASK) +#define ID_AA64MMFR0_BIGEND_EL0_FIXED (0x0 << ID_AA64MMFR0_BIGEND_EL0_SHIFT) +#define ID_AA64MMFR0_BIGEND_EL0_MIXED (0x1 << ID_AA64MMFR0_BIGEND_EL0_SHIFT) +#define ID_AA64MMFR0_TGRAN16_SHIFT 20 +#define ID_AA64MMFR0_TGRAN16_MASK (0xf << ID_AA64MMFR0_TGRAN16_SHIFT) +#define ID_AA64MMFR0_TGRAN16(x) ((x) & ID_AA64MMFR0_TGRAN16_MASK) +#define ID_AA64MMFR0_TGRAN16_NONE (0x0 << ID_AA64MMFR0_TGRAN16_SHIFT) +#define ID_AA64MMFR0_TGRAN16_IMPL (0x1 << ID_AA64MMFR0_TGRAN16_SHIFT) +#define ID_AA64MMFR0_TGRAN64_SHIFT 24 +#define ID_AA64MMFR0_TGRAN64_MASK (0xf << ID_AA64MMFR0_TGRAN64_SHIFT) +#define ID_AA64MMFR0_TGRAN64(x) ((x) & ID_AA64MMFR0_TGRAN64_MASK) +#define ID_AA64MMFR0_TGRAN64_IMPL (0x0 << ID_AA64MMFR0_TGRAN64_SHIFT) +#define ID_AA64MMFR0_TGRAN64_NONE (0xf << ID_AA64MMFR0_TGRAN64_SHIFT) +#define ID_AA64MMFR0_TGRAN4_SHIFT 28 +#define ID_AA64MMFR0_TGRAN4_MASK (0xf << ID_AA64MMFR0_TGRAN4_SHIFT) +#define ID_AA64MMFR0_TGRAN4(x) ((x) & ID_AA64MMFR0_TGRAN4_MASK) +#define ID_AA64MMFR0_TGRAN4_IMPL (0x0 << ID_AA64MMFR0_TGRAN4_SHIFT) +#define ID_AA64MMFR0_TGRAN4_NONE (0xf << ID_AA64MMFR0_TGRAN4_SHIFT) + /* ID_AA64PFR0_EL1 */ -#define ID_AA64PFR0_EL0_MASK (0xf << 0) -#define ID_AA64PFR0_EL1_MASK (0xf << 4) -#define ID_AA64PFR0_EL2_MASK (0xf << 8) -#define ID_AA64PFR0_EL3_MASK (0xf << 12) -#define ID_AA64PFR0_FP_MASK (0xf << 16) -#define ID_AA64PFR0_FP_IMPL (0x0 << 16) /* Floating-point implemented */ -#define ID_AA64PFR0_FP_NONE (0xf << 16) /* Floating-point not implemented */ -#define ID_AA64PFR0_ADV_SIMD_MASK (0xf << 20) -#define ID_AA64PFR0_GIC_SHIFT (24) -#define ID_AA64PFR0_GIC_BITS (0x4) /* Number of bits in GIC field */ -#define ID_AA64PFR0_GIC_MASK (0xf << ID_AA64PFR0_GIC_SHIFT) -#define ID_AA64PFR0_GIC_CPUIF_EN (0x1 << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_MASK 0x0fffffff +#define ID_AA64PFR0_EL0_SHIFT 0 +#define ID_AA64PFR0_EL0_MASK (0xf << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL0(x) ((x) & ID_AA64PFR0_EL0_MASK) +#define ID_AA64PFR0_EL0_64 (1 << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL0_64_32 (2 << ID_AA64PFR0_EL0_SHIFT) +#define ID_AA64PFR0_EL1_SHIFT 4 +#define ID_AA64PFR0_EL1_MASK (0xf << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL1(x) ((x) & ID_AA64PFR0_EL1_MASK) +#define ID_AA64PFR0_EL1_64 (1 << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL1_64_32 (2 << ID_AA64PFR0_EL1_SHIFT) +#define ID_AA64PFR0_EL2_SHIFT 8 +#define ID_AA64PFR0_EL2_MASK (0xf << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2(x) ((x) & ID_AA64PFR0_EL2_MASK) +#define ID_AA64PFR0_EL2_NONE (0 << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2_64 (1 << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL2_64_32 (2 << ID_AA64PFR0_EL2_SHIFT) +#define ID_AA64PFR0_EL3_SHIFT 12 +#define ID_AA64PFR0_EL3_MASK (0xf << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3(x) ((x) & ID_AA64PFR0_EL3_MASK) +#define ID_AA64PFR0_EL3_NONE (0 << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3_64 (1 << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_EL3_64_32 (2 << ID_AA64PFR0_EL3_SHIFT) +#define ID_AA64PFR0_FP_SHIFT 16 +#define ID_AA64PFR0_FP_MASK (0xf << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_FP(x) ((x) & ID_AA64PFR0_FP_MASK) +#define ID_AA64PFR0_FP_IMPL (0x0 << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_FP_NONE (0xf << ID_AA64PFR0_FP_SHIFT) +#define ID_AA64PFR0_ADV_SIMD_SHIFT 20 +#define ID_AA64PFR0_ADV_SIMD_MASK (0xf << ID_AA64PFR0_ADV_SIMD_SHIFT) +#define ID_AA64PFR0_ADV_SIMD(x) ((x) & ID_AA64PFR0_ADV_SIMD_MASK) +#define ID_AA64PFR0_ADV_SIMD_IMPL (0x0 << ID_AA64PFR0_ADV_SIMD_SHIFT) +#define ID_AA64PFR0_ADV_SIMD_NONE (0xf << ID_AA64PFR0_ADV_SIMD_SHIFT) +#define ID_AA64PFR0_GIC_BITS 0x4 /* Number of bits in GIC field */ +#define ID_AA64PFR0_GIC_SHIFT 24 +#define ID_AA64PFR0_GIC_MASK (0xf << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_GIC(x) ((x) & ID_AA64PFR0_GIC_MASK) +#define ID_AA64PFR0_GIC_CPUIF_NONE (0x0 << ID_AA64PFR0_GIC_SHIFT) +#define ID_AA64PFR0_GIC_CPUIF_EN (0x1 << ID_AA64PFR0_GIC_SHIFT) /* MAIR_EL1 - Memory Attribute Indirection Register */ #define MAIR_ATTR_MASK(idx) (0xff << ((n)* 8)) #define MAIR_ATTR(attr, idx) ((attr) << ((idx) * 8)) /* SCTLR_EL1 - System Control Register */ #define SCTLR_RES0 0xc8222400 /* Reserved, write 0 */ #define SCTLR_RES1 0x30d00800 /* Reserved, write 1 */ #define SCTLR_M 0x00000001 #define SCTLR_A 0x00000002 #define SCTLR_C 0x00000004 #define SCTLR_SA 0x00000008 #define SCTLR_SA0 0x00000010 #define SCTLR_CP15BEN 0x00000020 #define SCTLR_THEE 0x00000040 #define SCTLR_ITD 0x00000080 #define SCTLR_SED 0x00000100 #define SCTLR_UMA 0x00000200 #define SCTLR_I 0x00001000 #define SCTLR_DZE 0x00004000 #define SCTLR_UCT 0x00008000 #define SCTLR_nTWI 0x00010000 #define SCTLR_nTWE 0x00040000 #define SCTLR_WXN 0x00080000 #define SCTLR_EOE 0x01000000 #define SCTLR_EE 0x02000000 #define SCTLR_UCI 0x04000000 /* SPSR_EL1 */ /* * When the exception is taken in AArch64: * M[4] is 0 for AArch64 mode * M[3:2] is the exception level * M[1] is unused * M[0] is the SP select: * 0: always SP0 * 1: current ELs SP */ #define PSR_M_EL0t 0x00000000 #define PSR_M_EL1t 0x00000004 #define PSR_M_EL1h 0x00000005 #define PSR_M_EL2t 0x00000008 #define PSR_M_EL2h 0x00000009 #define PSR_M_MASK 0x0000001f #define PSR_F 0x00000040 #define PSR_I 0x00000080 #define PSR_A 0x00000100 #define PSR_D 0x00000200 #define PSR_IL 0x00100000 #define PSR_SS 0x00200000 #define PSR_V 0x10000000 #define PSR_C 0x20000000 #define PSR_Z 0x40000000 #define PSR_N 0x80000000 /* TCR_EL1 - Translation Control Register */ #define TCR_ASID_16 (1 << 36) #define TCR_IPS_SHIFT 32 #define TCR_IPS_32BIT (0 << TCR_IPS_SHIFT) #define TCR_IPS_36BIT (1 << TCR_IPS_SHIFT) #define TCR_IPS_40BIT (2 << TCR_IPS_SHIFT) #define TCR_IPS_42BIT (3 << TCR_IPS_SHIFT) #define TCR_IPS_44BIT (4 << TCR_IPS_SHIFT) #define TCR_IPS_48BIT (5 << TCR_IPS_SHIFT) #define TCR_TG1_SHIFT 30 #define TCR_TG1_16K (1 << TCR_TG1_SHIFT) #define TCR_TG1_4K (2 << TCR_TG1_SHIFT) #define TCR_TG1_64K (3 << TCR_TG1_SHIFT) #define TCR_SH1_SHIFT 28 #define TCR_SH1_IS (0x3UL << TCR_SH1_SHIFT) #define TCR_ORGN1_SHIFT 26 #define TCR_ORGN1_WBWA (0x1UL << TCR_ORGN1_SHIFT) #define TCR_IRGN1_SHIFT 24 #define TCR_IRGN1_WBWA (0x1UL << TCR_IRGN1_SHIFT) #define TCR_SH0_SHIFT 12 #define TCR_SH0_IS (0x3UL << TCR_SH0_SHIFT) #define TCR_ORGN0_SHIFT 10 #define TCR_ORGN0_WBWA (0x1UL << TCR_ORGN0_SHIFT) #define TCR_IRGN0_SHIFT 8 #define TCR_IRGN0_WBWA (0x1UL << TCR_IRGN0_SHIFT) #define TCR_CACHE_ATTRS ((TCR_IRGN0_WBWA | TCR_IRGN1_WBWA) |\ (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA)) #ifdef SMP #define TCR_SMP_ATTRS (TCR_SH0_IS | TCR_SH1_IS) #else #define TCR_SMP_ATTRS 0 #endif #define TCR_T1SZ_SHIFT 16 #define TCR_T0SZ_SHIFT 0 #define TCR_T1SZ(x) ((x) << TCR_T1SZ_SHIFT) #define TCR_T0SZ(x) ((x) << TCR_T0SZ_SHIFT) #define TCR_TxSZ(x) (TCR_T1SZ(x) | TCR_T0SZ(x)) /* Saved Program Status Register */ #define DBG_SPSR_SS (0x1 << 21) /* Monitor Debug System Control Register */ #define DBG_MDSCR_SS (0x1 << 0) #define DBG_MDSCR_KDE (0x1 << 13) #define DBG_MDSCR_MDE (0x1 << 15) /* Perfomance Monitoring Counters */ #define PMCR_E (1 << 0) /* Enable all counters */ #define PMCR_P (1 << 1) /* Reset all counters */ #define PMCR_C (1 << 2) /* Clock counter reset */ #define PMCR_D (1 << 3) /* CNTR counts every 64 clk cycles */ #define PMCR_X (1 << 4) /* Export to ext. monitoring (ETM) */ #define PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ #define PMCR_LC (1 << 6) /* Long cycle count enable */ #define PMCR_IMP_SHIFT 24 /* Implementer code */ #define PMCR_IMP_MASK (0xff << PMCR_IMP_SHIFT) #define PMCR_IDCODE_SHIFT 16 /* Identification code */ #define PMCR_IDCODE_MASK (0xff << PMCR_IDCODE_SHIFT) #define PMCR_IDCODE_CORTEX_A57 0x01 #define PMCR_IDCODE_CORTEX_A72 0x02 #define PMCR_IDCODE_CORTEX_A53 0x03 #define PMCR_N_SHIFT 11 /* Number of counters implemented */ #define PMCR_N_MASK (0x1f << PMCR_N_SHIFT) #endif /* !_MACHINE_ARMREG_H_ */ Index: projects/clang380-import/sys/arm64/include/cpu.h =================================================================== --- projects/clang380-import/sys/arm64/include/cpu.h (revision 293015) +++ projects/clang380-import/sys/arm64/include/cpu.h (revision 293016) @@ -1,184 +1,185 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Portions of this software were developed by Andrew Turner * under sponsorship from the FreeBSD Foundation * * 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. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 * from: FreeBSD: src/sys/i386/include/cpu.h,v 1.62 2001/06/29 * $FreeBSD$ */ #ifndef _MACHINE_CPU_H_ #define _MACHINE_CPU_H_ #include #include #include #define TRAPF_PC(tfp) ((tfp)->tf_lr) #define TRAPF_USERMODE(tfp) (((tfp)->tf_elr & (1ul << 63)) == 0) #define cpu_getstack(td) ((td)->td_frame->tf_sp) #define cpu_setstack(td, sp) ((td)->td_frame->tf_sp = (sp)) #define cpu_spinwait() /* nothing */ /* Extract CPU affinity levels 0-3 */ #define CPU_AFF0(mpidr) (u_int)(((mpidr) >> 0) & 0xff) #define CPU_AFF1(mpidr) (u_int)(((mpidr) >> 8) & 0xff) #define CPU_AFF2(mpidr) (u_int)(((mpidr) >> 16) & 0xff) #define CPU_AFF3(mpidr) (u_int)(((mpidr) >> 32) & 0xff) #define CPU_AFF0_MASK 0xffUL #define CPU_AFF1_MASK 0xff00UL #define CPU_AFF2_MASK 0xff0000UL #define CPU_AFF3_MASK 0xff00000000UL #define CPU_AFF_MASK (CPU_AFF0_MASK | CPU_AFF1_MASK | \ CPU_AFF2_MASK| CPU_AFF3_MASK) /* Mask affinity fields in MPIDR_EL1 */ #ifdef _KERNEL #define CPU_IMPL_ARM 0x41 #define CPU_IMPL_BROADCOM 0x42 #define CPU_IMPL_CAVIUM 0x43 #define CPU_IMPL_DEC 0x44 #define CPU_IMPL_INFINEON 0x49 #define CPU_IMPL_FREESCALE 0x4D #define CPU_IMPL_NVIDIA 0x4E #define CPU_IMPL_APM 0x50 #define CPU_IMPL_QUALCOMM 0x51 #define CPU_IMPL_MARVELL 0x56 #define CPU_IMPL_INTEL 0x69 #define CPU_PART_THUNDER 0x0A1 #define CPU_PART_FOUNDATION 0xD00 #define CPU_PART_CORTEX_A53 0xD03 #define CPU_PART_CORTEX_A57 0xD07 #define CPU_REV_THUNDER_1_0 0x00 #define CPU_REV_THUNDER_1_1 0x01 #define CPU_IMPL(midr) (((midr) >> 24) & 0xff) #define CPU_PART(midr) (((midr) >> 4) & 0xfff) #define CPU_VAR(midr) (((midr) >> 20) & 0xf) #define CPU_REV(midr) (((midr) >> 0) & 0xf) #define CPU_IMPL_TO_MIDR(val) (((val) & 0xff) << 24) #define CPU_PART_TO_MIDR(val) (((val) & 0xfff) << 4) #define CPU_VAR_TO_MIDR(val) (((val) & 0xf) << 20) #define CPU_REV_TO_MIDR(val) (((val) & 0xf) << 0) #define CPU_IMPL_MASK (0xff << 24) #define CPU_PART_MASK (0xfff << 4) #define CPU_VAR_MASK (0xf << 20) #define CPU_REV_MASK (0xf << 0) #define CPU_ID_RAW(impl, part, var, rev) \ (CPU_IMPL_TO_MIDR((impl)) | \ CPU_PART_TO_MIDR((part)) | CPU_VAR_TO_MIDR((var)) | \ CPU_REV_TO_MIDR((rev))) #define CPU_MATCH(mask, impl, part, var, rev) \ (((mask) & PCPU_GET(midr)) == \ ((mask) & CPU_ID_RAW((impl), (part), (var), (rev)))) #define CPU_MATCH_RAW(mask, devid) \ (((mask) & PCPU_GET(midr)) == ((mask) & (devid))) /* * Chip-specific errata. This defines are intended to be * booleans used within if statements. When an appropriate * kernel option is disabled, these defines must be defined * as 0 to allow the compiler to remove a dead code thus * produce better optimized kernel image. */ /* * Vendor: Cavium * Chip: ThunderX * Revision(s): Pass 1.0, Pass 1.1 */ #ifdef THUNDERX_PASS_1_1_ERRATA #define CPU_MATCH_ERRATA_CAVIUM_THUNDER_1_1 \ (CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \ CPU_IMPL_CAVIUM, CPU_PART_THUNDER, 0, CPU_REV_THUNDER_1_0) || \ CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK | CPU_REV_MASK, \ CPU_IMPL_CAVIUM, CPU_PART_THUNDER, 0, CPU_REV_THUNDER_1_1)) #else #define CPU_MATCH_ERRATA_CAVIUM_THUNDER_1_1 0 #endif extern char btext[]; extern char etext[]; extern uint64_t __cpu_affinity[]; void cpu_halt(void) __dead2; void cpu_reset(void) __dead2; void fork_trampoline(void); void identify_cpu(void); +void print_cpu_features(u_int); void swi_vm(void *v); #define CPU_AFFINITY(cpu) __cpu_affinity[(cpu)] #define CPU_CURRENT_SOCKET \ (CPU_AFF2(CPU_AFFINITY(PCPU_GET(cpuid)))) static __inline uint64_t get_cyclecount(void) { uint64_t ret; ret = READ_SPECIALREG(cntvct_el0); return (ret); } #define ADDRESS_TRANSLATE_FUNC(stage) \ static inline uint64_t \ arm64_address_translate_ ##stage (uint64_t addr) \ { \ uint64_t ret; \ \ __asm __volatile( \ "at " __STRING(stage) ", %1 \n" \ "mrs %0, par_el1" : "=r"(ret) : "r"(addr)); \ \ return (ret); \ } ADDRESS_TRANSLATE_FUNC(s1e0r) ADDRESS_TRANSLATE_FUNC(s1e0w) ADDRESS_TRANSLATE_FUNC(s1e1r) ADDRESS_TRANSLATE_FUNC(s1e1w) #endif #endif /* !_MACHINE_CPU_H_ */ Index: projects/clang380-import/sys/boot/forth/loader.conf =================================================================== --- projects/clang380-import/sys/boot/forth/loader.conf (revision 293015) +++ projects/clang380-import/sys/boot/forth/loader.conf (revision 293016) @@ -1,570 +1,571 @@ # This is loader.conf - a file full of useful variables that you can # set to change the default load behavior of your system. You should # not edit this file! Put any overrides into one of the # loader_conf_files instead and you will be able to update these # defaults later without spamming your local configuration information. # # All arguments must be in double quotes. # # $FreeBSD$ ############################################################## ### Basic configuration options ############################ ############################################################## exec="echo Loading /boot/defaults/loader.conf" kernel="kernel" # /boot sub-directory containing kernel and modules bootfile="kernel" # Kernel name (possibly absolute path) kernel_options="" # Flags to be passed to the kernel loader_conf_files="/boot/device.hints /boot/loader.conf /boot/loader.conf.local" nextboot_conf="/boot/nextboot.conf" nextboot_enable="NO" verbose_loading="NO" # Set to YES for verbose loader output ############################################################## ### Splash screen configuration ############################ ############################################################## splash_bmp_load="NO" # Set this to YES for bmp splash screen! splash_pcx_load="NO" # Set this to YES for pcx splash screen! splash_txt_load="NO" # Set this to YES for TheDraw splash screen! vesa_load="NO" # Set this to YES to load the vesa module bitmap_load="NO" # Set this to YES if you want splash screen! bitmap_name="splash.bmp" # Set this to the name of the file bitmap_type="splash_image_data" # and place it on the module_path ############################################################## ### Random number generator configuration ################## ############################################################## # See rc.conf(5). The entropy_boot_file config variable must agree with the # settings below. entropy_cache_load="YES" # Set this to NO to disable loading # entropy at boot time entropy_cache_name="/boot/entropy" # Set this to the name of the file entropy_cache_type="/boot/entropy" # Required for the kernel to find # the boot-time entropy cache. This # must not change value even if the # _name above does change! ############################################################## ### RAM Blacklist configuration ############################ ############################################################## ram_blacklist_load="NO" # Set this to YES to load a file # containing a list of addresses to # exclude from the running system. ram_blacklist_name="/boot/blacklist.txt" # Set this to the name of the file ram_blacklist_type="ram_blacklist" # Required for the kernel to find # the blacklist module ############################################################## ### Initial memory disk settings ########################### ############################################################## #initmd_load="YES" # The "initmd" prefix is arbitrary. #initmd_type="md_image" # Create md(4) disk at boot. #initmd_name="/boot/root.img" # Path to a file containing the image. #rootdev="ufs:/dev/md0" # Set the root filesystem to md(4) device. ############################################################## ### Loader settings ######################################## ############################################################## #loader_delay="3" # Delay in seconds before loading anything. # Default is unset and disabled (no delay). #autoboot_delay="10" # Delay in seconds before autobooting, # set to -1 if you don't want user to be # allowed to interrupt autoboot process and # escape to the loader prompt, set to # "NO" to disable autobooting #password="" # Prevent changes to boot options #bootlock_password="" # Prevent booting (see check-password.4th(8)) #geom_eli_passphrase_prompt="NO" # Prompt for geli(8) passphrase to mount root +bootenv_autolist="YES" # Auto populate the list of ZFS Boot Environments #beastie_disable="NO" # Turn the beastie boot menu on and off #kernels="kernel kernel.old" # Kernels to display in the boot menu #loader_logo="orbbw" # Desired logo: orbbw, orb, fbsdbw, beastiebw, beastie, none #comconsole_speed="9600" # Set the current serial console speed #console="vidconsole" # A comma separated list of console(s) #currdev="disk1s1a" # Set the current device module_path="/boot/modules" # Set the module search path #prompt="\\${interpret}" # Set the command prompt #root_disk_unit="0" # Force the root disk unit number #rootdev="disk1s1a" # Set the root filesystem #tftp.blksize="1428" # Set the RFC 2348 TFTP block size. # If the TFTP server does not support RFC 2348, # the block size is set to 512. If the value # is out of range ( < 8 || > 9008 ) an error is # returned. #twiddle_divisor="1" # >1 means slow down the progress indicator. ############################################################## ### Kernel settings ######################################## ############################################################## # The following boot_ variables are enabled by setting them to any value. # Their presence in the kernel environment (see kenv(1)) has the same # effect as setting the given boot flag (see boot(8)). #boot_askname="" # -a: Prompt the user for the name of the root device #boot_cdrom="" # -C: Attempt to mount root file system from CD-ROM #boot_ddb="" # -d: Instructs the kernel to start in the DDB debugger #boot_dfltroot="" # -r: Use the statically configured root file system #boot_gdb="" # -g: Selects gdb-remote mode for the kernel debugger #boot_multicons="" # -D: Use multiple consoles #boot_mute="" # -m: Mute the console #boot_pause="" # -p: Pause after each line during device probing #boot_serial="" # -h: Use serial console #boot_single="" # -s: Start system in single-user mode #boot_verbose="" # -v: Causes extra debugging information to be printed #init_path="/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init" # Sets the list of init candidates #init_shell="/bin/sh" # The shell binary used by init(8). #init_script="" # Initial script to run by init(8) before chrooting. #init_chroot="" # Directory for init(8) to chroot into. ############################################################## ### Kernel tunables ######################################## ############################################################## #hw.physmem="1G" # Limit physical memory. See loader(8) #kern.dfldsiz="" # Set the initial data size limit #kern.dflssiz="" # Set the initial stack size limit #kern.hz="100" # Set the kernel interval timer rate #kern.maxbcache="" # Set the max buffer cache KVA storage #kern.maxdsiz="" # Set the max data size #kern.maxfiles="" # Set the sys. wide open files limit #kern.maxproc="" # Set the maximum # of processes #kern.maxssiz="" # Set the max stack size #kern.maxswzone="" # Set the max swmeta KVA storage #kern.maxtsiz="" # Set the max text size #kern.maxusers="32" # Set size of various static tables #kern.msgbufsize="65536" # Set size of kernel message buffer #kern.nbuf="" # Set the number of buffer headers #kern.ncallout="" # Set the maximum # of timer events #kern.ngroups="1023" # Set the maximum # of supplemental groups #kern.sgrowsiz="" # Set the amount to grow stack #kern.cam.boot_delay="10000" # Delay (in ms) of root mount for CAM bus # registration, useful for USB sticks as root #kern.cam.scsi_delay="2000" # Delay (in ms) before probing SCSI #kern.ipc.maxsockets="" # Set the maximum number of sockets avaliable #kern.ipc.nmbclusters="" # Set the number of mbuf clusters #kern.ipc.nsfbufs="" # Set the number of sendfile(2) bufs #net.inet.tcp.tcbhashsize="" # Set the value of TCBHASHSIZE #vfs.root.mountfrom="" # Specify root partition in a way the # kernel understands #vm.kmem_size="" # Sets the size of kernel memory (bytes) #debug.kdb.break_to_debugger="0" # Allow console to break into debugger. #debug.ktr.cpumask="0xf" # Bitmask of CPUs to enable KTR on #debug.ktr.mask="0x1200" # Bitmask of KTR events to enable #debug.ktr.verbose="1" # Enable console dump of KTR events #net.graph.maxalloc="128" # Maximum number of queue items to allocate ############################################################## ### ATA modules ############################################ ############################################################## ataacard_load="NO" # ACARD ataacerlabs_load="NO" # Acer Labs Inc. (ALI) ataamd_load="NO" # American Micro Devices (AMD) ataati_load="NO" # ATI atacenatek_load="NO" # Cenatek atacypress_load="NO" # Cypress atacyrix_load="NO" # Cyrix atahighpoint_load="NO" # HighPoint ataintel_load="NO" # Intel ataite_load="NO" # Integrated Technology Inc. (ITE) atajmicron_load="NO" # JMicron atamarvell_load="NO" # Marvell atamicron_load="NO" # Micron atanational_load="NO" # National atanetcell_load="NO" # NetCell atanvidia_load="NO" # nVidia atapromise_load="NO" # Promise ataserverworks_load="NO" # ServerWorks atasiliconimage_load="NO" # Silicon Image Inc. (SiI) (formerly CMD) atasis_load="NO" # Silicon Integrated Systems Corp.(SiS) atavia_load="NO" # VIA Technologies Inc. ############################################################## ### Filesystem and related modules ######################### ############################################################## # Filesystems cd9660_load="NO" # ISO 9660 filesystem fdescfs_load="NO" # Filedescriptors filesystem linprocfs_load="NO" # Linux compatibility process filesystem linsysfs_load="NO" # Linux compatibility system filesystem msdosfs_load="NO" # FAT-12/16/32 nfsclient_load="NO" # NFS client nfsserver_load="NO" # NFS server nullfs_load="NO" # Null filesystem procfs_load="NO" # Process filesystem reiserfs_load="NO" # ReiserFS unionfs_load="NO" # Union filesystem zfs_load="NO" # ZFS # Related stuff geom_bde_load="NO" # Disk encryption driver (see gbde(4,8)) geom_ccd_load="NO" # Concatenated disk driver (see ccd(4), # ccdconfig(8)) geom_concat_load="NO" # Concatenated disk driver (see gconcat(8)) geom_eli_load="NO" # Disk encryption driver (see geli(8)) geom_gate_load="NO" # Userland disk driver (see geom_gate(4), # ggatec(8), ggated(8), ggatel(8)) geom_journal_load="NO" # Journaled filesystem driver (see gjournal(8)) geom_label_load="NO" # File system labels (see glabel(8)) geom_md_load="NO" # Memory disk driver (vnode/swap/malloc) (see # md(4), mdconfig(8)) geom_mirror_load="NO" # RAID1 disk driver (see gmirror(8)) geom_mountver_load="NO" # Mount verification disk driver geom_nop_load="NO" # Transparent disk driver (see gnop(8)) geom_raid3_load="NO" # RAID3 disk driver (see graid3(8)) geom_shsec_load="NO" # Shared secret disk driver (see gshsec(8)) geom_stripe_load="NO" # RAID0 disk driver (see gstripe(8)) geom_uncompress_load="NO" # Compressed disk images driver (see mkulzma(8)) geom_uzip_load="NO" # Compressed disk images driver (see mkuzip(8)) geom_vinum_load="NO" # Concatenated/mirror/raid driver (see vinum(4)) ############################################################## ### FireWire modules ####################################### ############################################################## firewire_load="NO" # IEEE1394 High-performance Serial Bus fwe_load="NO" # Ethernet emulation driver for FireWire fwip_load="NO" # IP over FireWire driver fwohci_load="NO" # OHCI FireWire chipset device driver sbp_load="NO" # SBP-2 Mass Storage Devices driver sbp_targ_load="NO" # SBP-2 Target mode ############################################################## ### Screen saver modules ################################### ############################################################## # This is best done in rc.conf screensave_load="NO" # Set to YES to load a screensaver module screensave_name="green_saver" # Set to the name of the screensaver module ############################################################## ### Emulation modules ###################################### ############################################################## cloudabi_load="NO" # Platform independent CloudABI support cloudabi64_load="NO" # 64-bit CloudABI executables support ibcs2_load="NO" # IBCS2 (SCO) emulation ibcs2_coff_load="NO" linux_load="NO" # Linux emulation svr4_load="NO" # SystemV R4 emulation streams_load="NO" # System V streams module ############################################################## ### Networking modules ##################################### ############################################################## if_disc_load="NO" # Discard device if_ef_load="NO" # pseudo-device providing support for multiple # ethernet frame types if_epair_load="NO" # Virtual b-t-b Ethernet-like interface pair if_gif_load="NO" # generic tunnel interface if_gre_load="NO" # encapsulating network device if_stf_load="NO" # 6to4 tunnel interface if_tap_load="NO" # Ethernet tunnel software network interface if_tun_load="NO" # Tunnel driver (user process ppp) if_vlan_load="NO" # IEEE 802.1Q VLAN network interface ipfw_load="NO" # Firewall pf_load="NO" # packet filter ############################################################## ### Networking drivers ##################################### ############################################################## bridgestp_load="NO" # if_bridge(4) support miibus_load="NO" # miibus support, needed for some drivers carp_load="NO" # carp(4) protocol if_ae_load="NO" # Attansic/Atheros L2 FastEthernet if_age_load="NO" # Attansic/Atheros L1 Gigabit Ethernet if_alc_load="NO" # Atheros AR8131/AR8132 Ethernet if_ale_load="NO" # Atheros AR8121/AR8113/AR8114 Ethernet if_an_load="NO" # Aironet 4500/4800 802.11 wireless NICs if_ath_load="NO" # Atheros IEEE 802.11 wireless NICs if_aue_load="NO" # ADMtek AN986 Pegasus USB Ethernet if_axe_load="NO" # ASIX Electronics AX88172 USB Ethernet if_bce_load="NO" # Broadcom NetXtreme II Gigabit Ethernet if_bfe_load="NO" # Broadcom BCM4401 if_bge_load="NO" # Broadcom BCM570x PCI Gigabit Ethernet if_bridge_load="NO" # if_bridge(4) devices if_bwi_load="NO" # Broadcom BCM53xx IEEE 802.11b/g wireness NICs if_bwn_load="NO" # Broadcom BCM43xx IEEE 802.11 wireless NICs if_bxe_load="NO" # Broadcom NetXtreme II 10Gb Ethernet if_cas_load="NO" # Sun Cassini/Cassini+ and NS DP83065 Saturn if_cm_load="NO" # SMC (90c26, 90c56, 90c66) if_cs_load="NO" # Crystal Semiconductor CS8920 if_cue_load="NO" # CATC USB-EL1210A USB Ethernet if_cxgb_load="NO" # Chelsio T3 10 Gigabit Ethernet if_dc_load="NO" # DEC/Intel 21143 and various workalikes if_de_load="NO" # DEC DC21x4x Ethernet if_ed_load="NO" # National Semiconductor DS8390/WD83C690 # Ethernet if_em_load="NO" # Intel(R) PRO/1000 Gigabit Ethernet if_en_load="NO" # Midway-based ATM interfaces if_ep_load="NO" # 3Com Etherlink III (3c5x9) if_et_load="NO" # Agere ET1310 10/100/Gigabit Ethernet if_ex_load="NO" # Intel EtherExpress Pro/10 Ethernet if_fatm_load="NO" # Fore PCA200E ATM if_fe_load="NO" # Fujitsu MB86960A/MB86965A based Ethernet # adapters if_fxp_load="NO" # Intel EtherExpress PRO/100B (82557, 82558) if_gem_load="NO" # Sun GEM/Sun ERI/Apple GMAC if_hatm_load="NO" # Fore/Marconi HE155 and HE622 if_hme_load="NO" # Sun Microelectronics STP2002-STQ Ethernet if_ie_load="NO" # Intel 82586 if_igb_load="NO" # Intel(R) PRO/1000 Gigabit Ethernet if_ipw_load="NO" # Intel PRO/Wireless 2100 wireless if_iwi_load="NO" # Intel PRO/Wireless 2200BG/2225BG/2915ABG # wireless if_iwn_load="NO" # Intel Wireless WiFi Link 802.11n wireless if_ixgb_load="NO" # Intel PRO/10Gb Ethernet if_ixgbe_load="NO" # Intel PRO/10Gb Ethernet PCI Express if_jme_load="NO" # JMicron JMC250 Gigabit/JMC260 Fast Ethernet if_lagg_load="NO" # lagg(4) devices if_le_load="NO" # AMD Am7900 LANCE and Am79C9xx PCnet if_lge_load="NO" # Level 1 LXT1001 NetCellerator PCI Gigabit # Ethernet if_malo_load="NO" # Marvell Libertas 88W8335 802.11 wireless # adapter if_msk_load="NO" # Marvell/SysKonnect Yukon II Gigabit Ethernet if_mxge_load="NO" # Myricom Myri10GE 10Gb Ethernet if_my_load="NO" # Myson PCI Fast Ethernet if_nfe_load="NO" # NVIDIA nForce MCP Networking Adapter if_nge_load="NO" # National Semiconductor PCI Gigabit Ethernet if_nxge_load="NO" # Neterion Xframe 10Gb Ethernet if_patm_load="NO" # IDT77252 ATM if_pcn_load="NO" # AMD PCnet PCI if_ral_load="NO" # Ralink Technology wireless if_re_load="NO" # RealTek 8139C+/8169/8169S/8110S if_rl_load="NO" # RealTek 8129/8139 if_rue_load="NO" # RealTek RTL8150 USB to Fast Ethernet if_rum_load="NO" # Ralink Technology USB 802.11a/b/g wireless if_run_load="NO" # Ralink Technology USB 802.11a/g/n wireless if_sbni_load="NO" # Granch SBNI12 leased line adapters if_sf_load="NO" # Adaptec Duralink PCI (AIC-6915 "starfire") if_sge_load="NO" # Silicon Integrated Systems SiS 190/191 if_sis_load="NO" # Silicon Integrated Systems SiS 900/7016 if_sk_load="NO" # SysKonnect SK-984x series PCI Gigabit Ethernet if_sn_load="NO" # SMC 91Cxx if_ste_load="NO" # Sundance Technologies ST201 Fast Ethernet if_stge_load="NO" # Sundance/Tamarack TC9021 Gigabit Ethernet if_ti_load="NO" # Alteon Networks Tigon 1 and Tigon 2 if_tl_load="NO" # Texas Instruments TNETE100 ("ThunderLAN") if_tx_load="NO" # SMC 83c17x Fast Ethernet if_txp_load="NO" # 3Com 3XP Typhoon/Sidewinder (3CR990) if_vge_load="NO" # VIA VT6122 PCI Gigabit Ethernet if_vte_load="NO" # DM&P Vortex86 RDC R6040 Fast Ethernet if_uath_load="NO" # Atheros USB wireless for AR5005UG & AR5005UX if_udav_load="NO" # Davicom DM9601 USB Ethernet if_upgt_load="NO" # Conexant/Intersil PrismGT USB wireless if_ural_load="NO" # Ralink Technology USB wireless if_urtw_load="NO" # Realtek 8187L USB wireless if_vr_load="NO" # VIA Rhine I and Rhine II if_vx_load="NO" # 3Com 3C590 family if_wb_load="NO" # Winbond W89C840F if_wi_load="NO" # WaveLAN/IEEE 802.11 wireless NICs if_wpi_load="NO" # Intel 3945ABG Wireless LAN IEEE 802.11 if_xe_load="NO" # Xircom CreditCard PCMCIA if_xl_load="NO" # 3Com Etherlink XL (3c900, 3c905, 3c905B) utopia_load="NO" # ATM PHY driver ############################################################## ### Netgraph modules ####################################### ############################################################## ng_UI_load="NO" # UI netgraph node type ng_async_load="NO" # asynchronous framing netgraph node type ng_bpf_load="NO" # Berkeley packet filter netgraph node type ng_bridge_load="NO" # Ethernet bridging netgraph node type ng_cisco_load="NO" # Cisco HDLC protocol netgraph node type ng_echo_load="NO" # Netgraph echo node type ng_eiface_load="NO" # generic Ethernet interface netgraph node type ng_etf_load="NO" # Ethertype filtering netgraph node type ng_ether_load="NO" # Ethernet netgraph node type ng_frame_relay_load="NO" # frame relay netgraph node type ng_gif_load="NO" # generic tunnel interface netgraph node type ng_gif_demux_load="NO" # demultiplexer for packets from ng_gif(4) nodes ng_hole_load="NO" # Netgraph discard node type ng_hub_load="NO" # packet distribution netgraph node type ng_iface_load="NO" # interface Netgraph node type ng_ip_input_load="NO" # netgraph IP input node type ng_ksocket_load="NO" # kernel socket netgraph node type ng_l2tp_load="NO" # L2TP protocol netgraph node type ng_lmi_load="NO" # frame relay LMI protocol netgraph node type ng_mppc_load="NO" # Microsoft MPPC/MPPE compression and # encryption netgraph node type ng_netflow_load="NO" # Cisco's NetFlow netgraph node type ng_one2many_load="NO" # packet multiplexing netgraph node type ng_ppp_load="NO" # PPP protocol netgraph node type ng_pppoe_load="NO" # RFC 2516 PPPOE protocol netgraph node type ng_pptpgre_load="NO" # PPTP GRE protocol netgraph node type ng_rfc1490_load="NO" # RFC 1490 netgraph node type ng_socket_load="NO" # Netgraph socket node type ng_split_load="NO" # netgraph node to separate incoming and # outgoing flows ng_sppp_load="NO" # sppp netgraph node type ng_tee_load="NO" # Netgraph ``tee'' node type ng_tty_load="NO" # Netgraph node type that is also a line # discipline ng_vjc_load="NO" # Van Jacobsen compression netgraph node type ng_vlan_load="NO" # IEEE 802.1Q VLAN tagging netgraph node type ############################################################## ### Sound modules ########################################## ############################################################## sound_load="NO" # Digital sound subsystem snd_ad1816_load="NO" # ad1816 snd_als4000_load="NO" # als4000 snd_atiixp_load="NO" # atiixp snd_cmi_load="NO" # cmi snd_cs4281_load="NO" # cs4281 snd_csa_load="NO" # csa snd_ds1_load="NO" # ds1 snd_emu10k1_load="NO" # Creative Sound Blaster Live snd_emu10kx_load="NO" # Creative SoundBlaster Live! and Audigy snd_envy24_load="NO" # VIA Envy24 snd_envy24ht_load="NO" # VIA Envy24HT snd_es137x_load="NO" # es137x snd_ess_load="NO" # ess snd_fm801_load="NO" # fm801 snd_hda_load="NO" # Intel High Definition Audio (Controller) snd_ich_load="NO" # Intel ICH snd_maestro_load="NO" # Maestro snd_maestro3_load="NO" # Maestro3 snd_mss_load="NO" # Mss snd_neomagic_load="NO" # Neomagic snd_sb16_load="NO" # Sound Blaster 16 snd_sb8_load="NO" # Sound Blaster Pro snd_sbc_load="NO" # Sbc snd_solo_load="NO" # Solo snd_spicds_load="NO" # SPI codecs snd_t4dwave_load="NO" # t4dwave snd_via8233_load="NO" # via8233 snd_via82c686_load="NO" # via82c686 snd_vibes_load="NO" # vibes snd_driver_load="NO" # All sound drivers ############################################################## ### USB modules ############################################ ############################################################## usb_load="NO" # USB subsystem udbp_load="NO" # USB double bulk pipe host 2 host cables ugen_load="NO" # USB generic device, if all else fails ... ucycom_load="NO" # Cyprus USB serial adapters ufm_load="NO" # Fm Radio uhid_load="NO" # Human Interface Devices ukbd_load="NO" # Keyboard ulpt_load="NO" # Printer ums_load="NO" # Mouse umass_load="NO" # Mass Storage Devices umct_load="NO" # Magic Control Technology USB-RS232 umodem_load="NO" # Modems uplcom_load="NO" # Prolific USB serial adapters urio_load="NO" # Rio MP3 players uvisor_load="NO" # PalmOS based PDAs if_aue_load="NO" # ADMtek USB ethernet if_axe_load="NO" # ASIX Electronics AX88172 USB ethernet if_cdce_load="NO" # Ethernet over USB (CDC) if_cue_load="NO" # CATC USB ethernet if_kue_load="NO" # Kawasaki LSI USB ethernet if_rae_load="NO" # Realtek RTL8150 USB adapter. if_rum_load="NO" # Ralink USB 802.11 wireless adapter if_uath_load="NO" # Atheros AR5523 wireless adapter if_run_load="NO" # Ralink USB 802.11 wireless adapter if_ural_load="NO" # Ralink RT2500USB 802.11 wireless adapter if_zyd_load="NO" # ZyDAS ZD1211(B) USB 802.11 wireless adapter snd_uaudio_load="NO" # USB audio ############################################################## ### Other modules ########################################## ############################################################## aio_load="NO" # Asynchronous I/O bktr_load="NO" # Brooktree Bt848/Bt878 TV/Video Capture Card ispfw_load="NO" # Qlogic ISP Firmware agp_load="NO" # agp module accf_data_load="NO" # Wait for data accept filter accf_dns_load="NO" # Wait for full DNS request accept filter accf_http_load="NO" # Wait for full HTTP request accept filter ppi_load="NO" # Interface to ppbus parallel 'geek' port pps_load="NO" # Pulse per second devices puc_load="NO" # PCI "Universal" Communications driver random_load="NO" # Random device speaker_load="NO" # AT speaker module coretemp_load="NO" # Intel Core CPU temperature monitor vkbd_load="NO" # Virtual AT keyboard interface vpd_load="NO" # Vital Product Data kernel interface vpo_load="NO" # Parallel to SCSI interface driver amdtemp_load="NO" # AMD K8/K10/K11 temperature monitor tpm_load="NO" # Trusted Platform Module wbwd_load="NO" # Winbond watchdog ############################################################## ### ACPI settings ########################################## ############################################################## acpi_dsdt_load="NO" # DSDT Overriding acpi_dsdt_type="acpi_dsdt" # Don't change this acpi_dsdt_name="/boot/acpi_dsdt.aml" # Override DSDT in BIOS by this file acpi_video_load="NO" # Load the ACPI video extension driver ############################################################## ### TrustedBSD MAC settings ################################ ############################################################## mac_biba_load="NO" # Biba MAC policy mac_bsdextended_load="NO" # BSD/extended MAC policy mac_ifoff="NO" # Interface silencing policy mac_mls_load="NO" # MLS MAC policy mac_none_load="NO" # Null MAC policy mac_partition_load="NO" # Partition MAC policy mac_seeotheruids_load="NO" # UID visbility MAC policy ############################################################## ### Module loading syntax example ########################## ############################################################## #module_load="YES" # loads module "module" #module_name="realname" # uses "realname" instead of "module" #module_type="type" # passes "-t type" to load #module_flags="flags" # passes "flags" to the module #module_before="cmd" # executes "cmd" before loading the module #module_after="cmd" # executes "cmd" after loading the module #module_error="cmd" # executes "cmd" if load fails Index: projects/clang380-import/sys/boot/forth/menu-commands.4th =================================================================== --- projects/clang380-import/sys/boot/forth/menu-commands.4th (revision 293015) +++ projects/clang380-import/sys/boot/forth/menu-commands.4th (revision 293016) @@ -1,354 +1,418 @@ \ Copyright (c) 2006-2015 Devin Teske \ 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$ marker task-menu-commands.4th include /boot/menusets.4th only forth definitions variable kernel_state variable root_state 0 kernel_state ! 0 root_state ! also menu-namespace also menu-command-helpers \ \ Boot \ : init_boot ( N -- N ) dup s" boot_single" getenv -1 <> if drop ( n n c-addr -- n n ) \ unused toggle_menuitem ( n n -- n n ) s" set menu_keycode[N]=115" \ base command to execute else s" set menu_keycode[N]=98" \ base command to execute then 17 +c! \ replace 'N' with ASCII numeral evaluate ; \ \ Alternate Boot \ : init_altboot ( N -- N ) dup s" boot_single" getenv -1 <> if drop ( n c-addr -- n ) \ unused toggle_menuitem ( n -- n ) s" set menu_keycode[N]=109" \ base command to execute else s" set menu_keycode[N]=115" \ base command to execute then 17 +c! \ replace 'N' with ASCII numeral evaluate ; : altboot ( N -- NOTREACHED ) s" boot_single" 2dup getenv -1 <> if drop ( c-addr/u c-addr -- c-addr/u ) \ unused unsetenv ( c-addr/u -- ) else 2drop ( c-addr/u -- ) \ unused s" set boot_single=YES" evaluate then 0 boot ( state -- ) ; \ \ ACPI \ : acpi_enable ( -- ) s" set acpi_load=YES" evaluate \ XXX deprecated but harmless s" set hint.acpi.0.disabled=0" evaluate s" loader.acpi_disabled_by_user" unsetenv ; : acpi_disable ( -- ) s" acpi_load" unsetenv \ XXX deprecated but harmless s" set hint.acpi.0.disabled=1" evaluate s" set loader.acpi_disabled_by_user=1" evaluate ; : toggle_acpi ( N -- N TRUE ) \ Make changes effective _before_ calling menu-redraw acpienabled? if acpi_disable else acpi_enable then menu-redraw TRUE \ loop menu again ; \ \ Safe Mode \ : safemode_enabled? ( -- flag ) s" kern.smp.disabled" getenv -1 <> dup if swap drop ( c-addr flag -- flag ) then ; : safemode_enable ( -- ) s" set kern.smp.disabled=1" evaluate s" set hw.ata.ata_dma=0" evaluate s" set hw.ata.atapi_dma=0" evaluate s" set hw.ata.wc=0" evaluate s" set hw.eisa_slots=0" evaluate s" set kern.eventtimer.periodic=1" evaluate s" set kern.geom.part.check_integrity=0" evaluate ; : safemode_disable ( -- ) s" kern.smp.disabled" unsetenv s" hw.ata.ata_dma" unsetenv s" hw.ata.atapi_dma" unsetenv s" hw.ata.wc" unsetenv s" hw.eisa_slots" unsetenv s" kern.eventtimer.periodic" unsetenv s" kern.geom.part.check_integrity" unsetenv ; : init_safemode ( N -- N ) safemode_enabled? if toggle_menuitem ( n -- n ) then ; : toggle_safemode ( N -- N TRUE ) toggle_menuitem \ Now we're going to make the change effective dup toggle_stateN @ 0= if safemode_disable else safemode_enable then menu-redraw TRUE \ loop menu again ; \ \ Single User Mode \ : singleuser_enabled? ( -- flag ) s" boot_single" getenv -1 <> dup if swap drop ( c-addr flag -- flag ) then ; : singleuser_enable ( -- ) s" set boot_single=YES" evaluate ; : singleuser_disable ( -- ) s" boot_single" unsetenv ; : init_singleuser ( N -- N ) singleuser_enabled? if toggle_menuitem ( n -- n ) then ; : toggle_singleuser ( N -- N TRUE ) toggle_menuitem menu-redraw \ Now we're going to make the change effective dup toggle_stateN @ 0= if singleuser_disable else singleuser_enable then TRUE \ loop menu again ; \ \ Verbose Boot \ : verbose_enabled? ( -- flag ) s" boot_verbose" getenv -1 <> dup if swap drop ( c-addr flag -- flag ) then ; : verbose_enable ( -- ) s" set boot_verbose=YES" evaluate ; : verbose_disable ( -- ) s" boot_verbose" unsetenv ; : init_verbose ( N -- N ) verbose_enabled? if toggle_menuitem ( n -- n ) then ; : toggle_verbose ( N -- N TRUE ) toggle_menuitem menu-redraw \ Now we're going to make the change effective dup toggle_stateN @ 0= if verbose_disable else verbose_enable then TRUE \ loop menu again ; \ \ Escape to Prompt \ : goto_prompt ( N -- N FALSE ) s" set autoboot_delay=NO" evaluate cr ." To get back to the menu, type `menu' and press ENTER" cr ." or type `boot' and press ENTER to start FreeBSD." cr cr FALSE \ exit the menu ; \ \ Cyclestate (used by kernel/root below) \ : init_cyclestate ( N K -- N ) over cycle_stateN ( n k -- n k addr ) begin tuck @ ( n k addr -- n addr k c ) over <> ( n addr k c -- n addr k 0|-1 ) while rot ( n addr k -- addr k n ) cycle_menuitem swap rot ( addr k n -- n k addr ) repeat 2drop ( n k addr -- n ) ; \ \ Kernel \ : init_kernel ( N -- N ) kernel_state @ ( n -- n k ) init_cyclestate ( n k -- n ) ; : activate_kernel ( N -- N ) dup cycle_stateN @ ( n -- n n2 ) dup kernel_state ! ( n n2 -- n n2 ) \ copy for re-initialization 48 + ( n n2 -- n n2' ) \ kernel_state to ASCII num s" set kernel=${kernel_prefix}${kernel[N]}${kernel_suffix}" 36 +c! ( n n2 c-addr/u -- n c-addr/u ) \ 'N' to ASCII num evaluate ( n c-addr/u -- n ) \ sets $kernel to full kernel-path ; : cycle_kernel ( N -- N TRUE ) cycle_menuitem \ cycle cycle_stateN to next value activate_kernel \ apply current cycle_stateN menu-redraw \ redraw menu TRUE \ loop menu again ; \ \ Root \ : init_root ( N -- N ) root_state @ ( n -- n k ) init_cyclestate ( n k -- n ) ; : activate_root ( N -- N ) dup cycle_stateN @ ( n -- n n2 ) dup root_state ! ( n n2 -- n n2 ) \ copy for re-initialization 48 + ( n n2 -- n n2' ) \ root_state to ASCII num s" set root=${root_prefix}${root[N]}${root_suffix}" 30 +c! ( n n2 c-addr/u -- n c-addr/u ) \ 'N' to ASCII num evaluate ( n c-addr/u -- n ) \ sets $root to full kernel-path ; : cycle_root ( N -- N TRUE ) cycle_menuitem \ cycle cycle_stateN to next value activate_root \ apply current cycle_stateN menu-redraw \ redraw menu TRUE \ loop menu again ; \ \ Menusets \ : goto_menu ( N M -- N TRUE ) menu-unset menuset-loadsetnum ( n m -- n ) menu-redraw TRUE \ Loop menu again ; \ \ Defaults \ : set_default_boot_options ( N -- N TRUE ) acpi_enable safemode_disable singleuser_disable verbose_disable 2 goto_menu ; +\ +\ Set boot environment defaults +\ + +: init_bootenv ( -- ) + s" set menu_caption[1]=${bemenu_current}${vfs.root.mountfrom}" evaluate + s" set ansi_caption[1]=${beansi_current}${vfs.root.mountfrom}" evaluate + s" set menu_caption[2]=${bemenu_bootfs}${zfs_be_active}" evaluate + s" set ansi_caption[2]=${beansi_bootfs}${zfs_be_active}" evaluate + s" set menu_caption[3]=${bemenu_page}${zfs_be_currpage}${bemenu_pageof}${zfs_be_pages}" evaluate + s" set ansi_caption[3]=${beansi_page}${zfs_be_currpage}${bemenu_pageof}${zfs_be_pages}" evaluate +; + +\ +\ Redraw the entire screen. A long BE name can corrupt the menu +\ + +: be_draw_screen + clear \ Clear the screen (in screen.4th) + print_version \ print version string (bottom-right; see version.4th) + draw-beastie \ Draw FreeBSD logo at right (in beastie.4th) + draw-brand \ Draw brand.4th logo at top (in brand.4th) + menu-init \ Initialize menu and draw bounding box (in menu.4th) +; + +\ +\ Select a boot environment +\ + +: set_bootenv ( N -- N TRUE ) + dup s" set vfs.root.mountfrom=${bootenv_root[E]}" 38 +c! evaluate + s" set currdev=${vfs.root.mountfrom}:" evaluate + s" unload" evaluate + free-module-options + s" /boot/defaults/loader.conf" read-conf + s" /boot/loader.conf" read-conf + s" /boot/loader.conf.local" read-conf + init_bootenv + be_draw_screen + menu-redraw + TRUE +; + +\ +\ Switch to the next page of boot environments +\ + +: set_be_page ( N -- N TRUE ) + s" zfs_be_currpage" getenv dup -1 = if + drop s" 1" + else + 0 s>d 2swap + >number ( ud caddr/u -- ud' caddr'/u' ) \ convert string to numbers + 2drop \ drop the string + 1 um/mod ( ud u1 -- u2 u3 ) \ convert double ud' to single u3' and remainder u2 + swap drop ( ud2 u3 -- u3 ) \ drop the remainder u2 + 1+ \ increment the page number + s>d <# #s #> \ convert back to a string + then + s" zfs_be_currpage" setenv + s" reloadbe" evaluate + 3 goto_menu +; + only forth definitions Index: projects/clang380-import/sys/boot/forth/menu.rc =================================================================== --- projects/clang380-import/sys/boot/forth/menu.rc (revision 293015) +++ projects/clang380-import/sys/boot/forth/menu.rc (revision 293016) @@ -1,134 +1,187 @@ \ Menu.rc \ $FreeBSD$ \ \ You should not edit this file! Put any overrides in menu.rc.local \ instead as this file can be replaced during system updates. \ \ Load required Forth modules include /boot/version.4th include /boot/brand.4th include /boot/menu.4th include /boot/menu-commands.4th include /boot/shortcuts.4th \ Screen prep clear \ clear the screen (see `screen.4th') print_version \ print version string (bottom-right; see `version.4th') draw-beastie \ draw freebsd mascot (on right; see `beastie.4th') draw-brand \ draw the FreeBSD title (top-left; see `brand.4th') menu-init \ initialize the menu area (see `menu.4th') \ Initialize main menu constructs (see `menu.4th') \ NOTE: To use `non-ansi' variants, add `loader_color=0' to loader.conf(5) \ NOTE: ANSI variants can use `^' in place of literal `Esc' (ASCII 27) \ \ MAIN MENU \ set menuset_name1="main" set mainmenu_init[1]="init_boot" set mainmenu_caption[1]="Boot Multi User [Enter]" set maintoggled_text[1]="Boot [S]ingle User [Enter]" set mainmenu_command[1]="boot" set mainansi_caption[1]="^[1mB^[moot Multi User ^[1m[Enter]^[m" set maintoggled_ansi[1]="Boot ^[1mS^[mingle User ^[1m[Enter]^[m" \ keycode set by init_boot set mainmenu_init[2]="init_altboot" set mainmenu_caption[2]="Boot [S]ingle User" set maintoggled_text[2]="Boot [M]ulti User" set mainmenu_command[2]="altboot" set mainansi_caption[2]="Boot ^[1mS^[mingle User" set maintoggled_ansi[2]="Boot ^[1mM^[multi User" \ keycode set by init_altboot set mainmenu_caption[3]="[Esc]ape to loader prompt" set mainmenu_command[3]="goto_prompt" set mainmenu_keycode[3]=27 set mainansi_caption[3]="^[1mEsc^[mape to loader prompt" \ Enable built-in "Reboot" trailing menuitem \ NOTE: appears before menu_options if configured \ set mainmenu_reboot \ Enable "Options:" separator. When set to a numerical value (1-8), a visual \ separator is inserted before that menuitem number. \ set mainmenu_options=5 set mainmenu_kernel=5 set mainmenu_command[5]="cycle_kernel" set mainmenu_keycode[5]=107 set mainmenu_caption[6]="Configure Boot [O]ptions..." set mainmenu_command[6]="2 goto_menu" set mainmenu_keycode[6]=111 set mainansi_caption[6]="Configure Boot ^[1mO^[mptions..." +s" currdev" getenv dup 0> [if] drop 4 s" zfs:" compare 0= [if] + set mainmenu_caption[7]="Select Boot [E]nvironment..." + set mainmenu_command[7]="3 goto_menu" + set mainmenu_keycode[7]=101 + set mainansi_caption[7]="Select Boot ^[1mE^[37mnvironment..." +[then] [else] drop [then] + \ \ BOOT OPTIONS MENU \ set menuset_name2="options" set optionsmenu_caption[1]="Back to Main Menu [Backspace]" set optionsmenu_command[1]="1 goto_menu" set optionsmenu_keycode[1]=8 set optionsansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[m" set optionsmenu_caption[2]="Load System [D]efaults" set optionsmenu_command[2]="set_default_boot_options" set optionsmenu_keycode[2]=100 set optionsansi_caption[2]="Load System ^[1mD^[mefaults" set optionsmenu_options=3 set optionsmenu_optionstext="Boot Options:" set optionsmenu_acpi=3 set optionsmenu_caption[3]="[A]CPI Support off" set optionstoggled_text[3]="[A]CPI Support On" set optionsmenu_command[3]="toggle_acpi" set optionsmenu_keycode[3]=97 set optionsansi_caption[3]="^[1mA^[mCPI Support ^[34;1mOff^[m" set optionstoggled_ansi[3]="^[1mA^[mCPI Support ^[32;7mOn^[m" set optionsmenu_init[4]="init_safemode" set optionsmenu_caption[4]="Safe [M]ode... off" set optionstoggled_text[4]="Safe [M]ode... On" set optionsmenu_command[4]="toggle_safemode" set optionsmenu_keycode[4]=109 set optionsansi_caption[4]="Safe ^[1mM^[mode... ^[34;1mOff^[m" set optionstoggled_ansi[4]="Safe ^[1mM^[mode... ^[32;7mOn^[m" set optionsmenu_init[5]="init_singleuser" set optionsmenu_caption[5]="[S]ingle User. off" set optionstoggled_text[5]="[S]ingle User. On" set optionsmenu_command[5]="toggle_singleuser" set optionsmenu_keycode[5]=115 set optionsansi_caption[5]="^[1mS^[mingle User. ^[34;1mOff^[m" set optionstoggled_ansi[5]="^[1mS^[mingle User. ^[32;7mOn^[m" set optionsmenu_init[6]="init_verbose" set optionsmenu_caption[6]="[V]erbose..... off" set optionstoggled_text[6]="[V]erbose..... On" set optionsmenu_command[6]="toggle_verbose" set optionsmenu_keycode[6]=118 set optionsansi_caption[6]="^[1mV^[merbose..... ^[34;1mOff^[m" set optionstoggled_ansi[6]="^[1mV^[merbose..... ^[32;7mOn^[m" +\ +\ BOOT ENVIRONMENT MENU +\ + +set menuset_name3="bootenv" + +set bemenu_current="Active: " +set beansi_current="^[1m${bemenu_current}^[m" +set bemenu_bootfs="bootfs: " +set beansi_bootfs="^[1m${bemenu_bootfs}^[m" +set bemenu_page="[P]age: " +set beansi_page="^[1mP^[mage: " +set bemenu_pageof=" of " +set beansi_pageof="${bemenu_pageof}" +set zfs_be_currpage=1 + +set bootenvmenu_init="init_bootenv" + +set bootenvmenu_command[1]="be_draw_screen 1 goto_menu" +set bootenvmenu_keycode[1]=8 + +set bootenvmenu_command[2]="set_bootenv" +set bootenvmenu_keycode[2]=97 +set bootenv_root[2]="${zfs_be_active}" + +set bootenvmenu_command[3]="set_be_page" +set bootenvmenu_keycode[3]=112 + +set bootenvmenu_options=4 +set bootenvmenu_optionstext="Boot Environments:" + \ Enable automatic booting (add ``autoboot_delay=N'' to loader.conf(5) to \ customize the timeout; default is 10-seconds) \ set menu_timeout_command="boot" \ Include optional elements defined in a local file \ try-include /boot/menu.rc.local + +\ Initialize boot environment variables +\ +s" reloadbe" sfind ( xt|0 bool ) [if] + s" bootenv_autolist" getenv dup -1 = [if] + drop s" execute" evaluate \ Use evaluate to avoid passing + \ reloadbe an optional parameter + [else] + s" YES" compare-insensitive 0= [if] + s" execute" evaluate + [then] + [then] +[else] + drop ( xt=0 ) +[then] \ Display the main menu (see `menu.4th') set menuset_initial=1 menuset-loadinitial menu-display Index: projects/clang380-import/sys/boot/forth/support.4th =================================================================== --- projects/clang380-import/sys/boot/forth/support.4th (revision 293015) +++ projects/clang380-import/sys/boot/forth/support.4th (revision 293016) @@ -1,1585 +1,1606 @@ \ Copyright (c) 1999 Daniel C. Sobral \ 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$ \ Loader.rc support functions: \ \ initialize ( addr len -- ) as above, plus load_conf_files \ load_conf ( addr len -- ) load conf file given \ include_conf_files ( -- ) load all conf files in load_conf_files \ print_syntax_error ( -- ) print line and marker of where a syntax \ error was detected \ print_line ( -- ) print last line processed \ load_kernel ( -- ) load kernel \ load_modules ( -- ) load modules flagged \ \ Exported structures: \ \ string counted string structure \ cell .addr string address \ cell .len string length \ module module loading information structure \ cell module.flag should we load it? \ string module.name module's name \ string module.loadname name to be used in loading the module \ string module.type module's type \ string module.args flags to be passed during load \ string module.beforeload command to be executed before load \ string module.afterload command to be executed after load \ string module.loaderror command to be executed if load fails \ cell module.next list chain \ \ Exported global variables; \ \ string conf_files configuration files to be loaded \ cell modules_options pointer to first module information \ value verbose? indicates if user wants a verbose loading \ value any_conf_read? indicates if a conf file was succesfully read \ \ Other exported words: \ note, strlen is internal \ strdup ( addr len -- addr' len) similar to strdup(3) \ strcat ( addr len addr' len' -- addr len+len' ) similar to strcat(3) \ s' ( | string' -- addr len | ) similar to s" \ rudimentary structure support \ Exception values 1 constant ESYNTAX 2 constant ENOMEM 3 constant EFREE 4 constant ESETERROR \ error setting environment variable 5 constant EREAD \ error reading 6 constant EOPEN 7 constant EEXEC \ XXX never catched 8 constant EBEFORELOAD 9 constant EAFTERLOAD \ I/O constants 0 constant SEEK_SET 1 constant SEEK_CUR 2 constant SEEK_END 0 constant O_RDONLY 1 constant O_WRONLY 2 constant O_RDWR \ Crude structure support : structure: create here 0 , ['] drop , 0 does> create here swap dup @ allot cell+ @ execute ; : member: create dup , over , + does> cell+ @ + ; : ;structure swap ! ; : constructor! >body cell+ ! ; : constructor: over :noname ; : ;constructor postpone ; swap cell+ ! ; immediate : sizeof ' >body @ state @ if postpone literal then ; immediate : offsetof ' >body cell+ @ state @ if postpone literal then ; immediate : ptr 1 cells member: ; : int 1 cells member: ; \ String structure structure: string ptr .addr int .len constructor: 0 over .addr ! 0 swap .len ! ;constructor ;structure \ Module options linked list structure: module int module.flag sizeof string member: module.name sizeof string member: module.loadname sizeof string member: module.type sizeof string member: module.args sizeof string member: module.beforeload sizeof string member: module.afterload sizeof string member: module.loaderror ptr module.next ;structure \ Internal loader structures (preloaded_file, kernel_module, file_metadata) \ must be in sync with the C struct in sys/boot/common/bootstrap.h structure: preloaded_file ptr pf.name ptr pf.type ptr pf.args ptr pf.metadata \ file_metadata int pf.loader int pf.addr int pf.size ptr pf.modules \ kernel_module ptr pf.next \ preloaded_file ;structure structure: kernel_module ptr km.name \ ptr km.args ptr km.fp \ preloaded_file ptr km.next \ kernel_module ;structure structure: file_metadata int md.size 2 member: md.type \ this is not ANS Forth compatible (XXX) ptr md.next \ file_metadata 0 member: md.data \ variable size ;structure \ end of structures \ Global variables string conf_files string nextboot_conf_file create module_options sizeof module.next allot 0 module_options ! create last_module_option sizeof module.next allot 0 last_module_option ! 0 value verbose? 0 value nextboot? \ Support string functions : strdup { addr len -- addr' len' } len allocate if ENOMEM throw then addr over len move len ; : strcat { addr len addr' len' -- addr len+len' } addr' addr len + len' move addr len len' + ; : strchr { addr len c -- addr' len' } begin len while addr c@ c = if addr len exit then addr 1 + to addr len 1 - to len repeat 0 0 ; : s' \ same as s", allows " in the string [char] ' parse state @ if postpone sliteral then ; immediate : 2>r postpone >r postpone >r ; immediate : 2r> postpone r> postpone r> ; immediate : 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate : getenv? getenv -1 = if false else drop true then ; \ determine if a word appears in a string, case-insensitive : contains? ( addr1 len1 addr2 len2 -- 0 | -1 ) 2 pick 0= if 2drop 2drop true exit then dup 0= if 2drop 2drop false exit then begin begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop while 1+ swap 1- repeat swap 2 pick 1- over < while 2over 2over drop over compare-insensitive 0= if 2 pick over = if 2drop 2drop true exit then 2 pick tuck - -rot + swap over c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if 2drop 2drop true exit then then begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if false else true then 2 pick 0> and while 1+ swap 1- repeat swap repeat 2drop 2drop false ; : boot_serial? ( -- 0 | -1 ) s" console" getenv dup -1 <> if s" comconsole" 2swap contains? else drop false then s" boot_serial" getenv dup -1 <> if swap drop 0> else drop false then or \ console contains comconsole ( or ) boot_serial s" boot_multicons" getenv dup -1 <> if swap drop 0> else drop false then or \ previous boolean ( or ) boot_multicons ; \ Private definitions vocabulary support-functions only forth also support-functions definitions \ Some control characters constants 7 constant bell 8 constant backspace 9 constant tab 10 constant lf 13 constant \ Read buffer size 80 constant read_buffer_size \ Standard suffixes : load_module_suffix s" _load" ; : module_loadname_suffix s" _name" ; : module_type_suffix s" _type" ; : module_args_suffix s" _flags" ; : module_beforeload_suffix s" _before" ; : module_afterload_suffix s" _after" ; : module_loaderror_suffix s" _error" ; \ Support operators : >= < 0= ; : <= > 0= ; \ Assorted support functions : free-memory free if EFREE throw then ; : strget { var -- addr len } var .addr @ var .len @ ; \ assign addr len to variable. : strset { addr len var -- } addr var .addr ! len var .len ! ; \ free memory and reset fields : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ; \ free old content, make a copy of the string and assign to variable : string= { addr len var -- } var strfree addr len strdup var strset ; : strtype ( str -- ) strget type ; \ assign a reference to what is on the stack : strref { addr len var -- addr len } addr var .addr ! len var .len ! addr len ; \ unquote a string : unquote ( addr len -- addr len ) over c@ [char] " = if 2 chars - swap char+ swap then ; \ Assignment data temporary storage string name_buffer string value_buffer \ Line by line file reading functions \ \ exported: \ line_buffer \ end_of_file? \ fd \ read_line \ reset_line_reading vocabulary line-reading also line-reading definitions \ File data temporary storage string read_buffer 0 value read_buffer_ptr \ File's line reading function get-current ( -- wid ) previous definitions string line_buffer 0 value end_of_file? variable fd >search ( wid -- ) definitions : skip_newlines begin read_buffer .len @ read_buffer_ptr > while read_buffer .addr @ read_buffer_ptr + c@ lf = if read_buffer_ptr char+ to read_buffer_ptr else exit then repeat ; : scan_buffer ( -- addr len ) read_buffer_ptr >r begin read_buffer .len @ r@ > while read_buffer .addr @ r@ + c@ lf = if read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr exit then r> char+ >r repeat read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr ; : line_buffer_resize ( len -- len ) >r line_buffer .len @ if line_buffer .addr @ line_buffer .len @ r@ + resize if ENOMEM throw then else r@ allocate if ENOMEM throw then then line_buffer .addr ! r> ; : append_to_line_buffer ( addr len -- ) line_buffer strget 2swap strcat line_buffer .len ! drop ; : read_from_buffer scan_buffer ( -- addr len ) line_buffer_resize ( len -- len ) append_to_line_buffer ( addr len -- ) ; : refill_required? read_buffer .len @ read_buffer_ptr = end_of_file? 0= and ; : refill_buffer 0 to read_buffer_ptr read_buffer .addr @ 0= if read_buffer_size allocate if ENOMEM throw then read_buffer .addr ! then fd @ read_buffer .addr @ read_buffer_size fread dup -1 = if EREAD throw then dup 0= if true to end_of_file? then read_buffer .len ! ; get-current ( -- wid ) previous definitions >search ( wid -- ) : reset_line_reading 0 to read_buffer_ptr ; : read_line line_buffer strfree skip_newlines begin read_from_buffer refill_required? while refill_buffer repeat ; only forth also support-functions definitions \ Conf file line parser: \ ::= '='[] | \ [] \ ::= {||'_'} \ ::= '"'{|'\'}'"' | \ ::= ASCII 32 to 126, except '\' and '"' \ ::= '#'{} \ \ exported: \ line_pointer \ process_conf 0 value line_pointer vocabulary file-processing also file-processing definitions \ parser functions \ \ exported: \ get_assignment vocabulary parser also parser definitions 0 value parsing_function 0 value end_of_line : end_of_line? line_pointer end_of_line = ; \ classifiers for various character classes in the input line : letter? line_pointer c@ >r r@ [char] A >= r@ [char] Z <= and r@ [char] a >= r> [char] z <= and or ; : digit? line_pointer c@ >r r@ [char] - = r@ [char] 0 >= r> [char] 9 <= and or ; : quote? line_pointer c@ [char] " = ; : assignment_sign? line_pointer c@ [char] = = ; : comment? line_pointer c@ [char] # = ; : space? line_pointer c@ bl = line_pointer c@ tab = or ; : backslash? line_pointer c@ [char] \ = ; : underscore? line_pointer c@ [char] _ = ; : dot? line_pointer c@ [char] . = ; \ manipulation of input line : skip_character line_pointer char+ to line_pointer ; : skip_to_end_of_line end_of_line to line_pointer ; : eat_space begin end_of_line? if 0 else space? then while skip_character repeat ; : parse_name ( -- addr len ) line_pointer begin end_of_line? if 0 else letter? digit? underscore? dot? or or or then while skip_character repeat line_pointer over - strdup ; : remove_backslashes { addr len | addr' len' -- addr' len' } len allocate if ENOMEM throw then to addr' addr >r begin addr c@ [char] \ <> if addr c@ addr' len' + c! len' char+ to len' then addr char+ to addr r@ len + addr = until r> drop addr' len' ; : parse_quote ( -- addr len ) line_pointer skip_character end_of_line? if ESYNTAX throw then begin quote? 0= while backslash? if skip_character end_of_line? if ESYNTAX throw then then skip_character end_of_line? if ESYNTAX throw then repeat skip_character line_pointer over - remove_backslashes ; : read_name parse_name ( -- addr len ) name_buffer strset ; : read_value quote? if parse_quote ( -- addr len ) else parse_name ( -- addr len ) then value_buffer strset ; : comment skip_to_end_of_line ; : white_space_4 eat_space comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; : variable_value read_value ['] white_space_4 to parsing_function ; : white_space_3 eat_space letter? digit? quote? or or if ['] variable_value to parsing_function exit then ESYNTAX throw ; : assignment_sign skip_character ['] white_space_3 to parsing_function ; : white_space_2 eat_space assignment_sign? if ['] assignment_sign to parsing_function exit then ESYNTAX throw ; : variable_name read_name ['] white_space_2 to parsing_function ; : white_space_1 eat_space letter? if ['] variable_name to parsing_function exit then comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; get-current ( -- wid ) previous definitions >search ( wid -- ) : get_assignment line_buffer strget + to end_of_line line_buffer .addr @ to line_pointer ['] white_space_1 to parsing_function begin end_of_line? 0= while parsing_function execute repeat parsing_function ['] comment = parsing_function ['] white_space_1 = parsing_function ['] white_space_4 = or or 0= if ESYNTAX throw then ; only forth also support-functions also file-processing definitions \ Process line : assignment_type? ( addr len -- flag ) name_buffer strget compare 0= ; : suffix_type? ( addr len -- flag ) name_buffer .len @ over <= if 2drop false exit then name_buffer .len @ over - name_buffer .addr @ + over compare 0= ; : loader_conf_files? s" loader_conf_files" assignment_type? ; : nextboot_flag? s" nextboot_enable" assignment_type? ; : nextboot_conf? s" nextboot_conf" assignment_type? ; : verbose_flag? s" verbose_loading" assignment_type? ; : execute? s" exec" assignment_type? ; : module_load? load_module_suffix suffix_type? ; : module_loadname? module_loadname_suffix suffix_type? ; : module_type? module_type_suffix suffix_type? ; : module_args? module_args_suffix suffix_type? ; : module_beforeload? module_beforeload_suffix suffix_type? ; : module_afterload? module_afterload_suffix suffix_type? ; : module_loaderror? module_loaderror_suffix suffix_type? ; \ build a 'set' statement and execute it : set_environment_variable name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string allocate if ENOMEM throw then dup 0 \ start with an empty string and append the pieces s" set " strcat name_buffer strget strcat s" =" strcat value_buffer strget strcat ['] evaluate catch if 2drop free drop ESETERROR throw else free-memory then ; : set_conf_files set_environment_variable s" loader_conf_files" getenv conf_files string= ; -: set_nextboot_conf \ XXX maybe do as set_conf_files ? +: set_nextboot_conf value_buffer strget unquote nextboot_conf_file string= ; : append_to_module_options_list ( addr -- ) module_options @ 0= if dup module_options ! last_module_option ! else dup last_module_option @ module.next ! last_module_option ! then ; : set_module_name { addr -- } \ check leaks name_buffer strget addr module.name string= ; : yes_value? value_buffer strget \ XXX could use unquote 2dup s' "YES"' compare >r 2dup s' "yes"' compare >r 2dup s" YES" compare >r s" yes" compare r> r> r> and and and 0= ; : find_module_option ( -- addr | 0 ) \ return ptr to entry matching name_buffer module_options @ begin dup while dup module.name strget name_buffer strget compare 0= if exit then module.next @ repeat ; : new_module_option ( -- addr ) sizeof module allocate if ENOMEM throw then dup sizeof module erase dup append_to_module_options_list dup set_module_name ; : get_module_option ( -- addr ) find_module_option ?dup 0= if new_module_option then ; : set_module_flag name_buffer .len @ load_module_suffix nip - name_buffer .len ! yes_value? get_module_option module.flag ! ; : set_module_args name_buffer .len @ module_args_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.args string= ; : set_module_loadname name_buffer .len @ module_loadname_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loadname string= ; : set_module_type name_buffer .len @ module_type_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.type string= ; : set_module_beforeload name_buffer .len @ module_beforeload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.beforeload string= ; : set_module_afterload name_buffer .len @ module_afterload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.afterload string= ; : set_module_loaderror name_buffer .len @ module_loaderror_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loaderror string= ; : set_nextboot_flag yes_value? to nextboot? ; : set_verbose yes_value? to verbose? ; : execute_command value_buffer strget unquote ['] evaluate catch if EEXEC throw then ; : process_assignment name_buffer .len @ 0= if exit then loader_conf_files? if set_conf_files exit then nextboot_flag? if set_nextboot_flag exit then nextboot_conf? if set_nextboot_conf exit then verbose_flag? if set_verbose exit then execute? if execute_command exit then module_load? if set_module_flag exit then module_loadname? if set_module_loadname exit then module_type? if set_module_type exit then module_args? if set_module_args exit then module_beforeload? if set_module_beforeload exit then module_afterload? if set_module_afterload exit then module_loaderror? if set_module_loaderror exit then set_environment_variable ; \ free_buffer ( -- ) \ \ Free some pointers if needed. The code then tests for errors \ in freeing, and throws an exception if needed. If a pointer is \ not allocated, it's value (0) is used as flag. : free_buffers name_buffer strfree value_buffer strfree ; \ Higher level file processing get-current ( -- wid ) previous definitions >search ( wid -- ) : process_conf begin end_of_file? 0= while free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch swap throw throw repeat ; : peek_file ( addr len -- ) 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch fd @ fclose swap throw throw ; only forth also support-functions definitions \ Interface to loading conf files : load_conf ( addr len -- ) - \ ." ----- Trying conf " 2dup type cr \ debugging 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then ['] process_conf catch fd @ fclose throw ; : print_line line_buffer strtype cr ; : print_syntax_error line_buffer strtype cr line_buffer .addr @ begin line_pointer over <> while bl emit char+ repeat drop ." ^" cr ; \ Debugging support functions only forth definitions also support-functions : test-file ['] load_conf catch dup . ESYNTAX = if cr print_syntax_error then ; \ find a module name, leave addr on the stack (0 if not found) : find-module ( -- ptr | 0 ) bl parse ( addr len ) module_options @ >r ( store current pointer ) begin r@ while 2dup ( addr len addr len ) r@ module.name strget compare 0= if drop drop r> exit then ( found it ) r> module.next @ >r repeat type ." was not found" cr r> ; : show-nonempty ( addr len mod -- ) strget dup verbose? or if 2swap type type cr else drop drop drop drop then ; : show-one-module { addr -- addr } ." Name: " addr module.name strtype cr s" Path: " addr module.loadname show-nonempty s" Type: " addr module.type show-nonempty s" Flags: " addr module.args show-nonempty s" Before load: " addr module.beforeload show-nonempty s" After load: " addr module.afterload show-nonempty s" Error: " addr module.loaderror show-nonempty ." Status: " addr module.flag @ if ." Load" else ." Don't load" then cr cr addr ; : show-module-options module_options @ begin ?dup while show-one-module module.next @ repeat ; +: free-one-module { addr -- addr } + addr module.name strfree + addr module.loadname strfree + addr module.type strfree + addr module.args strfree + addr module.beforeload strfree + addr module.afterload strfree + addr module.loaderror strfree + addr +; + +: free-module-options + module_options @ + begin + ?dup + while + free-one-module + dup module.next @ + swap free-memory + repeat + 0 module_options ! + 0 last_module_option ! +; + only forth also support-functions definitions \ Variables used for processing multiple conf files string current_file_name_ref \ used to print the file name \ Indicates if any conf file was succesfully read 0 value any_conf_read? \ loader_conf_files processing support functions : get_conf_files ( -- addr len ) \ put addr/len on stack, reset var - \ ." -- starting on <" conf_files strtype ." >" cr \ debugging conf_files strget 0 0 conf_files strset ; : skip_leading_spaces { addr len pos -- addr len pos' } begin pos len = if 0 else addr pos + c@ bl = then while pos char+ to pos repeat addr len pos ; \ return the file name at pos, or free the string if nothing left : get_file_name { addr len pos -- addr len pos' addr' len' || 0 } pos len = if addr free abort" Fatal error freeing memory" 0 exit then pos >r begin \ stay in the loop until have chars and they are not blank pos len = if 0 else addr pos + c@ bl <> then while pos char+ to pos repeat addr len pos addr r@ + pos r> - - \ 2dup ." get_file_name has " type cr \ debugging ; : get_next_file ( addr len ptr -- addr len ptr' addr' len' | 0 ) skip_leading_spaces get_file_name ; : print_current_file current_file_name_ref strtype ; : process_conf_errors dup 0= if true to any_conf_read? drop exit then >r 2drop r> dup ESYNTAX = if ." Warning: syntax error on file " print_current_file cr print_syntax_error drop exit then dup ESETERROR = if ." Warning: bad definition on file " print_current_file cr print_line drop exit then dup EREAD = if ." Warning: error reading file " print_current_file cr drop exit then dup EOPEN = if verbose? if ." Warning: unable to open file " print_current_file cr then drop exit then dup EFREE = abort" Fatal error freeing memory" dup ENOMEM = abort" Out of memory" throw \ Unknown error -- pass ahead ; \ Process loader_conf_files recursively \ Interface to loader_conf_files processing : include_conf_files get_conf_files 0 ( addr len offset ) begin get_next_file ?dup ( addr len 1 | 0 ) while current_file_name_ref strref ['] load_conf catch process_conf_errors conf_files .addr @ if recurse then repeat ; : get_nextboot_conf_file ( -- addr len ) - nextboot_conf_file strget strdup + nextboot_conf_file strget ; : rewrite_nextboot_file ( -- ) get_nextboot_conf_file O_WRONLY fopen fd ! fd @ -1 = if EOPEN throw then fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop fd @ fclose ; : include_nextboot_file ( -- ) get_nextboot_conf_file ['] peek_file catch if 2drop then nextboot? if get_nextboot_conf_file current_file_name_ref strref ['] load_conf catch process_conf_errors ['] rewrite_nextboot_file catch if 2drop then then ; \ Module loading functions : load_parameters { addr -- addr addrN lenN ... addr1 len1 N } addr addr module.args strget addr module.loadname .len @ if addr module.loadname strget else addr module.name strget then addr module.type .len @ if addr module.type strget s" -t " 4 ( -t type name flags ) else 2 ( name flags ) then ; : before_load ( addr -- addr ) dup module.beforeload .len @ if dup module.beforeload strget ['] evaluate catch if EBEFORELOAD throw then then ; : after_load ( addr -- addr ) dup module.afterload .len @ if dup module.afterload strget ['] evaluate catch if EAFTERLOAD throw then then ; : load_error ( addr -- addr ) dup module.loaderror .len @ if dup module.loaderror strget evaluate \ This we do not intercept so it can throw errors then ; : pre_load_message ( addr -- addr ) verbose? if dup module.name strtype ." ..." then ; : load_error_message verbose? if ." failed!" cr then ; : load_succesful_message verbose? if ." ok" cr then ; : load_module load_parameters load ; : process_module ( addr -- addr ) pre_load_message before_load begin ['] load_module catch if dup module.loaderror .len @ if load_error \ Command should return a flag! else load_error_message true \ Do not retry then else after_load load_succesful_message true \ Succesful, do not retry then until ; : process_module_errors ( addr ior -- ) dup EBEFORELOAD = if drop ." Module " dup module.name strtype dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.beforeload strtype cr \ XXX there was a typo here abort then dup EAFTERLOAD = if drop ." Module " dup module.name .addr @ over module.name .len @ type dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.afterload strtype cr abort then throw \ Don't know what it is all about -- pass ahead ; \ Module loading interface \ scan the list of modules, load enabled ones. : load_modules ( -- ) ( throws: abort & user-defined ) module_options @ ( list_head ) begin ?dup while dup module.flag @ if ['] process_module catch process_module_errors then module.next @ repeat ; \ h00h00 magic used to try loading either a kernel with a given name, \ or a kernel with the default name in a directory of a given name \ (the pain!) : bootpath s" /boot/" ; : modulepath s" module_path" ; \ Functions used to save and restore module_path's value. : saveenv ( addr len | -1 -- addr' len | 0 -1 ) dup -1 = if 0 swap exit then strdup ; : freeenv ( addr len | 0 -1 ) -1 = if drop else free abort" Freeing error" then ; : restoreenv ( addr len | 0 -1 -- ) dup -1 = if ( it wasn't set ) 2drop modulepath unsetenv else over >r modulepath setenv r> free abort" Freeing error" then ; : clip_args \ Drop second string if only one argument is passed 1 = if 2swap 2drop 1 else 2 then ; also builtins \ Parse filename from a semicolon-separated list \ replacement, not working yet : newparse-; { addr len | a1 -- a' len-x addr x } addr len [char] ; strchr dup if ( a1 len1 ) swap to a1 ( store address ) 1 - a1 @ 1 + swap ( remove match ) addr a1 addr - else 0 0 addr len then ; : parse-; ( addr len -- addr' len-x addr x ) over 0 2swap ( addr 0 addr len ) begin dup 0 <> ( addr 0 addr len ) while over c@ [char] ; <> ( addr 0 addr len flag ) while 1- swap 1+ swap 2swap 1+ 2swap repeat then dup 0 <> if 1- swap 1+ swap then 2swap ; \ Try loading one of multiple kernels specified : try_multiple_kernels ( addr len addr' len' args -- flag ) >r begin parse-; 2>r 2over 2r> r@ clip_args s" DEBUG" getenv? if s" echo Module_path: ${module_path}" evaluate ." Kernel : " >r 2dup type r> cr dup 2 = if ." Flags : " >r 2over type r> cr then then 1 load while dup 0= until 1 >r \ Failure else 0 >r \ Success then 2drop 2drop r> r> drop ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if available. If not, dummy values must be given. \ \ The kernel gets loaded from the current module_path. : load_a_kernel ( flags len 1 | x x 0 -- flag ) local args 2local flags 0 0 2local kernel end-locals \ Check if a default kernel name exists at all, exits if not s" bootfile" getenv dup -1 <> if to kernel flags kernel args 1+ try_multiple_kernels dup 0= if exit then then drop s" kernel" getenv dup -1 <> if to kernel else drop 1 exit \ Failure then \ Try all default kernel names flags kernel args 1+ try_multiple_kernels ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ The module_path variable is overridden if load is succesful, by \ prepending the successful path. : load_from_directory ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags 0 0 2local oldmodulepath \ like a string 0 0 2local newmodulepath \ like a string end-locals \ Set the environment variable module_path, and try loading \ the kernel again. modulepath getenv saveenv to oldmodulepath \ Try prepending /boot/ first bootpath nip path nip + \ total length oldmodulepath nip dup -1 = if drop else 1+ + \ add oldpath -- XXX why the 1+ ? then allocate if ( out of memory ) 1 exit then \ XXX throw ? 0 bootpath strcat path strcat 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel 0= if ( success ) oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 exit then \ Well, try without the prepended /boot/ path newmodulepath drop swap move newmodulepath drop path nip 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel if ( failed once more ) oldmodulepath restoreenv newmodulepath drop free-memory 1 else oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 then ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ 3. The "path" argument \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ Unless "path" is meant to be kernel name itself. In that case, it \ will first be tried as a full path, and, next, search on the \ directories pointed by module_path. \ \ The module_path variable is overridden if load is succesful, by \ prepending the successful path. : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags end-locals \ First, assume path is an absolute path to a directory flags path args clip_args load_from_directory dup 0= if exit else drop then \ Next, assume path points to the kernel flags path args try_multiple_kernels ; : initialize ( addr len -- ) strdup conf_files strset ; : kernel_options ( -- addr len 1 | 0 ) s" kernel_options" getenv dup -1 = if drop 0 else 1 then ; : standard_kernel_search ( flags 1 | 0 -- flag ) local args args 0= if 0 0 then 2local flags s" kernel" getenv dup -1 = if 0 swap then 2local path end-locals path nip -1 = if ( there isn't a "kernel" environment variable ) flags args load_a_kernel else flags path args 1+ clip_args load_directory_or_file then ; : load_kernel ( -- ) ( throws: abort ) kernel_options standard_kernel_search abort" Unable to load a kernel!" ; : load_xen ( -- flag ) s" xen_kernel" getenv dup -1 <> if 1 1 load ( c-addr/u flag N -- flag ) else drop 0 ( -1 -- flag ) then ; : load_xen_throw ( -- ) ( throws: abort ) load_xen abort" Unable to load Xen!" ; : set_defaultoptions ( -- ) s" kernel_options" getenv dup -1 = if drop else s" temp_options" setenv then ; \ pick the i-th argument, i starts at 0 : argv[] ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 ) 2dup = if 0 0 exit then \ out of range dup >r 1+ 2* ( skip N and ui ) pick r> 1+ 2* ( skip N and ai ) pick ; : drop_args ( aN uN ... a1 u1 N -- ) 0 ?do 2drop loop ; : argc dup ; : queue_argv ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 ) >r over 2* 1+ -roll r> over 2* 1+ -roll 1+ ; : unqueue_argv ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 ) 1- -rot ; \ compute the length of the buffer including the spaces between words : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len ) dup 0= if 0 exit then 0 >r \ Size 0 >r \ Index begin argc r@ <> while r@ argv[] nip r> r> rot + 1+ >r 1+ >r repeat r> drop r> ; : concat_argv ( aN uN ... a1 u1 N -- a u ) strlen(argv) allocate if ENOMEM throw then 0 2>r ( save addr 0 on return stack ) begin dup while unqueue_argv ( ... N a1 u1 ) 2r> 2swap ( old a1 u1 ) strcat s" " strcat ( append one space ) \ XXX this gives a trailing space 2>r ( store string on the result stack ) repeat drop_args 2r> ; : set_tempoptions ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 ) \ Save the first argument, if it exists and is not a flag argc if 0 argv[] drop c@ [char] - <> if unqueue_argv 2>r \ Filename 1 >r \ Filename present else 0 >r \ Filename not present then else 0 >r \ Filename not present then \ If there are other arguments, assume they are flags ?dup if concat_argv 2dup s" temp_options" setenv drop free if EFREE throw then else set_defaultoptions then \ Bring back the filename, if one was provided r> if 2r> 1 else 0 then ; : get_arguments ( -- addrN lenN ... addr1 len1 N ) 0 begin \ Get next word on the command line parse-word ?dup while queue_argv repeat drop ( empty string ) ; : load_kernel_and_modules ( args -- flag ) set_tempoptions argc >r s" temp_options" getenv dup -1 <> if queue_argv else drop then load_xen ?dup 0= if ( success ) r> if ( a path was passed ) load_directory_or_file else standard_kernel_search then ?dup 0= if ['] load_modules catch then then ; only forth definitions Index: projects/clang380-import/sys/boot/i386/loader/main.c =================================================================== --- projects/clang380-import/sys/boot/i386/loader/main.c (revision 293015) +++ projects/clang380-import/sys/boot/i386/loader/main.c (revision 293016) @@ -1,393 +1,450 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); /* * MD bootstrap main() and assorted miscellaneous * commands. */ #include #include #include #include #include #include #include #include "bootstrap.h" #include "common/bootargs.h" #include "libi386/libi386.h" #include "libi386/smbios.h" #include "btxv86.h" #ifdef LOADER_ZFS_SUPPORT #include "../zfs/libzfs.h" #endif CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE); CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO); CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS); CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE); /* Arguments passed in from the boot1/boot2 loader */ static struct bootargs *kargs; static u_int32_t initial_howto; static u_int32_t initial_bootdev; static struct bootinfo *initial_bootinfo; struct arch_switch archsw; /* MI/MD interface boundary */ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); #ifdef LOADER_ZFS_SUPPORT +static void init_zfs_bootenv(char *currdev); static void i386_zfs_probe(void); #endif /* from vers.c */ extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[]; /* XXX debugging */ extern char end[]; static void *heap_top; static void *heap_bottom; int main(void) { int i; /* Pick up arguments */ kargs = (void *)__args; initial_howto = kargs->howto; initial_bootdev = kargs->bootdev; initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL; /* Initialize the v86 register set to a known-good state. */ bzero(&v86, sizeof(v86)); v86.efl = PSL_RESERVED_DEFAULT | PSL_I; /* * Initialise the heap as early as possible. Once this is done, malloc() is usable. */ bios_getmem(); #if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \ defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT) if (high_heap_size > 0) { heap_top = PTOV(high_heap_base + high_heap_size); heap_bottom = PTOV(high_heap_base); if (high_heap_base < memtop_copyin) memtop_copyin = high_heap_base; } else #endif { heap_top = (void *)PTOV(bios_basemem); heap_bottom = (void *)end; } setheap(heap_bottom, heap_top); /* * XXX Chicken-and-egg problem; we want to have console output early, but some * console attributes may depend on reading from eg. the boot device, which we * can't do yet. * * We can use printf() etc. once this is done. * If the previous boot stage has requested a serial console, prefer that. */ bi_setboothowto(initial_howto); if (initial_howto & RB_MULTIPLE) { if (initial_howto & RB_SERIAL) setenv("console", "comconsole vidconsole", 1); else setenv("console", "vidconsole comconsole", 1); } else if (initial_howto & RB_SERIAL) setenv("console", "comconsole", 1); else if (initial_howto & RB_MUTE) setenv("console", "nullconsole", 1); cons_probe(); /* * Initialise the block cache */ bcache_init(32, 512); /* 16k cache XXX tune this */ /* * Special handling for PXE and CD booting. */ if (kargs->bootinfo == 0) { /* * We only want the PXE disk to try to init itself in the below * walk through devsw if we actually booted off of PXE. */ if (kargs->bootflags & KARGS_FLAGS_PXE) pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL); else if (kargs->bootflags & KARGS_FLAGS_CD) bc_add(initial_bootdev); } archsw.arch_autoload = i386_autoload; archsw.arch_getdev = i386_getdev; archsw.arch_copyin = i386_copyin; archsw.arch_copyout = i386_copyout; archsw.arch_readin = i386_readin; archsw.arch_isainb = isa_inb; archsw.arch_isaoutb = isa_outb; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; #endif /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024); if (initial_bootinfo != NULL) { initial_bootinfo->bi_basemem = bios_basemem / 1024; initial_bootinfo->bi_extmem = bios_extmem / 1024; } /* detect ACPI for future reference */ biosacpi_detect(); /* detect SMBIOS for future reference */ smbios_detect(NULL); /* detect PCI BIOS for future reference */ biospci_detect(); printf("\n"); printf("%s, Revision %s\n", bootprog_name, bootprog_rev); printf("(%s, %s)\n", bootprog_maker, bootprog_date); extract_currdev(); /* set $currdev and $loaddev */ setenv("LINES", "24", 1); /* optional */ bios_getsmap(); interact(NULL); /* if we ever get here, it is an error */ return (1); } /* * Set the 'current device' by (if possible) recovering the boot device as * supplied by the initial bootstrap. * * XXX should be extended for netbooting. */ static void extract_currdev(void) { struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; struct zfs_boot_args *zargs; #endif int biosdev = -1; /* Assume we are booting from a BIOS disk by default */ new_currdev.d_dev = &biosdisk; /* new-style boot loaders such as pxeldr and cdldr */ if (kargs->bootinfo == 0) { if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) { /* we are booting from a CD with cdboot */ new_currdev.d_dev = &bioscd; new_currdev.d_unit = bc_bios2unit(initial_bootdev); } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) { /* we are booting from pxeldr */ new_currdev.d_dev = &pxedisk; new_currdev.d_unit = 0; } else { /* we don't know what our boot device is */ new_currdev.d_kind.biosdisk.slice = -1; new_currdev.d_kind.biosdisk.partition = 0; biosdev = -1; } #ifdef LOADER_ZFS_SUPPORT } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { zargs = NULL; /* check for new style extended argument */ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) zargs = (struct zfs_boot_args *)(kargs + 1); if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) { /* sufficient data is provided */ new_currdev.d_kind.zfs.pool_guid = zargs->pool; new_currdev.d_kind.zfs.root_guid = zargs->root; if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) { sprintf(buf, "%llu", zargs->primary_pool); setenv("vfs.zfs.boot.primary_pool", buf, 1); sprintf(buf, "%llu", zargs->primary_vdev); setenv("vfs.zfs.boot.primary_vdev", buf, 1); } } else { /* old style zfsboot block */ new_currdev.d_kind.zfs.pool_guid = kargs->zfspool; new_currdev.d_kind.zfs.root_guid = 0; } new_currdev.d_dev = &zfs_dev; #endif } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) { /* The passed-in boot device is bad */ new_currdev.d_kind.biosdisk.slice = -1; new_currdev.d_kind.biosdisk.partition = 0; biosdev = -1; } else { new_currdev.d_kind.biosdisk.slice = B_SLICE(initial_bootdev) - 1; new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev); biosdev = initial_bootinfo->bi_bios_dev; /* * If we are booted by an old bootstrap, we have to guess at the BIOS * unit number. We will lose if there is more than one disk type * and we are not booting from the lowest-numbered disk type * (ie. SCSI when IDE also exists). */ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) /* biosdev doesn't match major */ biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */ } new_currdev.d_type = new_currdev.d_dev->dv_type; /* * If we are booting off of a BIOS disk and we didn't succeed in determining * which one we booted off of, just use disk0: as a reasonable default. */ if ((new_currdev.d_type == biosdisk.dv_type) && ((new_currdev.d_unit = bd_bios2unit(biosdev)) == -1)) { printf("Can't work out which disk we are booting from.\n" "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev); new_currdev.d_unit = 0; } +#ifdef LOADER_ZFS_SUPPORT + init_zfs_bootenv(zfs_fmtdev(&new_currdev)); +#endif + env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), i386_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, env_nounset); } +#ifdef LOADER_ZFS_SUPPORT +static void +init_zfs_bootenv(char *currdev) +{ + char *beroot; + + /* Remove the trailing : */ + currdev[strlen(currdev) - 1] = '\0'; + setenv("zfs_be_active", currdev, 1); + /* Do not overwrite if already set */ + setenv("vfs.root.mountfrom", currdev, 0); + /* Forward past zfs: */ + currdev = strchr(currdev, ':'); + currdev++; + /* Remove the last element (current bootenv) */ + beroot = strrchr(currdev, '/'); + beroot[0] = '\0'; + + beroot = currdev; + + setenv("zfs_be_root", beroot, 1); +} +#endif + COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); printf("Rebooting...\n"); delay(1000000); __exit(0); } /* provide this for panic, as it's not in the startup code */ void exit(int code) { __exit(code); } COMMAND_SET(heap, "heap", "show heap usage", command_heap); static int command_heap(int argc, char *argv[]) { mallocstats(); printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom, sbrk(0), heap_top); return(CMD_OK); } #ifdef LOADER_ZFS_SUPPORT COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", command_lszfs); static int command_lszfs(int argc, char *argv[]) { int err; if (argc != 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } err = zfs_list(argv[1]); if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } + + return (CMD_OK); +} + +COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", + command_reloadbe); + +static int +command_reloadbe(int argc, char *argv[]) +{ + int err; + + if (argc > 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + if (argc == 2) { + err = zfs_bootenv(argv[1]); + } else { + err = zfs_bootenv(getenv("zfs_be_root")); + } + + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + return (CMD_OK); } #endif /* ISA bus access functions for PnP. */ static int isa_inb(int port) { return (inb(port)); } static void isa_outb(int port, int value) { outb(port, value); } #ifdef LOADER_ZFS_SUPPORT static void i386_zfs_probe(void) { char devname[32]; int unit; /* * Open all the disks we can find and see if we can reconstruct * ZFS pools from them. */ for (unit = 0; unit < MAXBDDEV; unit++) { if (bd_unit2bios(unit) == -1) break; sprintf(devname, "disk%d:", unit); zfs_probe_dev(devname, NULL); } } #endif Index: projects/clang380-import/sys/boot/zfs/libzfs.h =================================================================== --- projects/clang380-import/sys/boot/zfs/libzfs.h (revision 293015) +++ projects/clang380-import/sys/boot/zfs/libzfs.h (revision 293016) @@ -1,69 +1,72 @@ /*- * Copyright (c) 2012 Andriy Gapon * 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$ */ #ifndef _BOOT_LIBZFS_H_ #define _BOOT_LIBZFS_H_ #define ZFS_MAXNAMELEN 256 /* * ZFS fully-qualified device descriptor. * Note, this must match the 'struct devdesc' declaration in bootstrap.h. * Arch-specific device descriptors should be binary compatible with this * structure if they are to support ZFS. */ struct zfs_devdesc { struct devsw *d_dev; int d_type; int d_unit; void *d_opendata; uint64_t pool_guid; uint64_t root_guid; }; struct zfs_boot_args { uint32_t size; uint32_t reserved; uint64_t pool; uint64_t root; uint64_t primary_pool; uint64_t primary_vdev; }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path); char *zfs_fmtdev(void *vdev); int zfs_probe_dev(const char *devname, uint64_t *pool_guid); int zfs_list(const char *name); +int zfs_bootenv(const char *name); +int zfs_belist_add(const char *name); +int zfs_set_env(void); extern struct devsw zfs_dev; extern struct fs_ops zfs_fsops; #endif /*_BOOT_LIBZFS_H_*/ Index: projects/clang380-import/sys/boot/zfs/zfs.c =================================================================== --- projects/clang380-import/sys/boot/zfs/zfs.c (revision 293015) +++ projects/clang380-import/sys/boot/zfs/zfs.c (revision 293016) @@ -1,696 +1,860 @@ /*- * Copyright (c) 2007 Doug Rabson * 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$ */ #include __FBSDID("$FreeBSD$"); /* * Stand-alone file reading package. */ #include #include #include #include #include #include #include #include #include #include #include "libzfs.h" #include "zfsimpl.c" +/* Define the range of indexes to be populated with ZFS Boot Environments */ +#define ZFS_BE_FIRST 4 +#define ZFS_BE_LAST 8 + static int zfs_open(const char *path, struct open_file *f); static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid); static int zfs_close(struct open_file *f); static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t zfs_seek(struct open_file *f, off_t offset, int where); static int zfs_stat(struct open_file *f, struct stat *sb); static int zfs_readdir(struct open_file *f, struct dirent *d); struct devsw zfs_dev; struct fs_ops zfs_fsops = { "zfs", zfs_open, zfs_close, zfs_read, zfs_write, zfs_seek, zfs_stat, zfs_readdir }; /* * In-core open file. */ struct file { off_t f_seekp; /* seek pointer */ dnode_phys_t f_dnode; uint64_t f_zap_type; /* zap type for readdir */ uint64_t f_num_leafs; /* number of fzap leaf blocks */ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ }; +static int zfs_env_index; +static int zfs_env_count; + +SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); +struct zfs_be_list *zfs_be_headp; +struct zfs_be_entry { + const char *name; + SLIST_ENTRY(zfs_be_entry) entries; +} *zfs_be, *zfs_be_tmp; + /* * Open a file. */ static int zfs_open(const char *upath, struct open_file *f) { struct zfsmount *mount = (struct zfsmount *)f->f_devdata; struct file *fp; int rc; if (f->f_dev != &zfs_dev) return (EINVAL); /* allocate file system specific data structure */ fp = malloc(sizeof(struct file)); bzero(fp, sizeof(struct file)); f->f_fsdata = (void *)fp; rc = zfs_lookup(mount, upath, &fp->f_dnode); fp->f_seekp = 0; if (rc) { f->f_fsdata = NULL; free(fp); } return (rc); } static int zfs_close(struct open_file *f) { struct file *fp = (struct file *)f->f_fsdata; dnode_cache_obj = 0; f->f_fsdata = (void *)0; if (fp == (struct file *)0) return (0); free(fp); return (0); } /* * Copy a portion of a file into kernel memory. * Cross block boundaries when necessary. */ static int zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; struct stat sb; size_t n; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); n = size; if (fp->f_seekp + n > sb.st_size) n = sb.st_size - fp->f_seekp; rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n); if (rc) return (rc); if (0) { int i; for (i = 0; i < n; i++) putchar(((char*) start)[i]); } fp->f_seekp += n; if (resid) *resid = size - n; return (0); } /* * Don't be silly - the bootstrap has no business writing anything. */ static int zfs_write(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { return (EROFS); } static off_t zfs_seek(struct open_file *f, off_t offset, int where) { struct file *fp = (struct file *)f->f_fsdata; switch (where) { case SEEK_SET: fp->f_seekp = offset; break; case SEEK_CUR: fp->f_seekp += offset; break; case SEEK_END: { struct stat sb; int error; error = zfs_stat(f, &sb); if (error != 0) { errno = error; return (-1); } fp->f_seekp = sb.st_size - offset; break; } default: errno = EINVAL; return (-1); } return (fp->f_seekp); } static int zfs_stat(struct open_file *f, struct stat *sb) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); } static int zfs_readdir(struct open_file *f, struct dirent *d) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; mzap_ent_phys_t mze; struct stat sb; size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); if (!S_ISDIR(sb.st_mode)) return (ENOTDIR); /* * If this is the first read, get the zap type. */ if (fp->f_seekp == 0) { rc = dnode_read(spa, &fp->f_dnode, 0, &fp->f_zap_type, sizeof(fp->f_zap_type)); if (rc) return (rc); if (fp->f_zap_type == ZBT_MICRO) { fp->f_seekp = offsetof(mzap_phys_t, mz_chunk); } else { rc = dnode_read(spa, &fp->f_dnode, offsetof(zap_phys_t, zap_num_leafs), &fp->f_num_leafs, sizeof(fp->f_num_leafs)); if (rc) return (rc); fp->f_seekp = bsize; fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } } if (fp->f_zap_type == ZBT_MICRO) { mzap_next: if (fp->f_seekp >= bsize) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, &mze, sizeof(mze)); if (rc) return (rc); fp->f_seekp += sizeof(mze); if (!mze.mze_name[0]) goto mzap_next; d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value); d->d_type = ZFS_DIRENT_TYPE(mze.mze_value); strcpy(d->d_name, mze.mze_name); d->d_namlen = strlen(d->d_name); return (0); } else { zap_leaf_t zl; zap_leaf_chunk_t *zc, *nc; int chunk; size_t namelen; char *p; uint64_t value; /* * Initialise this so we can use the ZAP size * calculating macros. */ zl.l_bs = ilog2(bsize); zl.l_phys = fp->f_zap_leaf; /* * Figure out which chunk we are currently looking at * and consider seeking to the next leaf. We use the * low bits of f_seekp as a simple chunk index. */ fzap_next: chunk = fp->f_seekp & (bsize - 1); if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) { fp->f_seekp = (fp->f_seekp & ~(bsize - 1)) + bsize; chunk = 0; /* * Check for EOF and read the new leaf. */ if (fp->f_seekp >= bsize * fp->f_num_leafs) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } zc = &ZAP_LEAF_CHUNK(&zl, chunk); fp->f_seekp++; if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) goto fzap_next; namelen = zc->l_entry.le_name_numints; if (namelen > sizeof(d->d_name)) namelen = sizeof(d->d_name); /* * Paste the name back together. */ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); p = d->d_name; while (namelen > 0) { int len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; memcpy(p, nc->l_array.la_array, len); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); } d->d_name[sizeof(d->d_name) - 1] = 0; /* * Assume the first eight bytes of the value are * a uint64_t. */ value = fzap_leaf_value(&zl, zc); d->d_fileno = ZFS_DIRENT_OBJ(value); d->d_type = ZFS_DIRENT_TYPE(value); d->d_namlen = strlen(d->d_name); return (0); } } static int vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size) { int fd; fd = (uintptr_t) priv; lseek(fd, offset, SEEK_SET); if (read(fd, buf, size) == size) { return 0; } else { return (EIO); } } static int zfs_dev_init(void) { spa_t *spa; spa_t *next; spa_t *prev; zfs_init(); if (archsw.arch_zfs_probe == NULL) return (ENXIO); archsw.arch_zfs_probe(); prev = NULL; spa = STAILQ_FIRST(&zfs_pools); while (spa != NULL) { next = STAILQ_NEXT(spa, spa_link); if (zfs_spa_init(spa)) { if (prev == NULL) STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); else STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link); } else prev = spa; spa = next; } return (0); } struct zfs_probe_args { int fd; const char *devname; uint64_t *pool_guid; uint16_t secsz; }; static int zfs_diskread(void *arg, void *buf, size_t blocks, off_t offset) { struct zfs_probe_args *ppa; ppa = (struct zfs_probe_args *)arg; return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd, offset * ppa->secsz, buf, blocks * ppa->secsz)); } static int zfs_probe(int fd, uint64_t *pool_guid) { spa_t *spa; int ret; ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); if (ret == 0 && pool_guid != NULL) *pool_guid = spa->spa_guid; return (ret); } static void zfs_probe_partition(void *arg, const char *partname, const struct ptable_entry *part) { struct zfs_probe_args *ppa, pa; struct ptable *table; char devname[32]; int ret; /* Probe only freebsd-zfs and freebsd partitions */ if (part->type != PART_FREEBSD && part->type != PART_FREEBSD_ZFS) return; ppa = (struct zfs_probe_args *)arg; strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); devname[strlen(ppa->devname) - 1] = '\0'; sprintf(devname, "%s%s:", devname, partname); pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return; ret = zfs_probe(pa.fd, ppa->pool_guid); if (ret == 0) return; /* Do we have BSD label here? */ if (part->type == PART_FREEBSD) { pa.devname = devname; pa.pool_guid = ppa->pool_guid; pa.secsz = ppa->secsz; table = ptable_open(&pa, part->end - part->start + 1, ppa->secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); } int zfs_probe_dev(const char *devname, uint64_t *pool_guid) { struct ptable *table; struct zfs_probe_args pa; off_t mediasz; int ret; pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return (ENXIO); /* Probe the whole disk */ ret = zfs_probe(pa.fd, pool_guid); if (ret == 0) return (0); /* Probe each partition */ ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); if (ret == 0) ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); if (ret == 0) { pa.devname = devname; pa.pool_guid = pool_guid; table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); return (0); } /* * Print information about ZFS pools */ static void zfs_dev_print(int verbose) { spa_t *spa; char line[80]; if (verbose) { spa_all_status(); return; } STAILQ_FOREACH(spa, &zfs_pools, spa_link) { sprintf(line, " zfs:%s\n", spa->spa_name); pager_output(line); } } /* * Attempt to open the pool described by (dev) for use by (f). */ static int zfs_dev_open(struct open_file *f, ...) { va_list args; struct zfs_devdesc *dev; struct zfsmount *mount; spa_t *spa; int rv; va_start(args, f); dev = va_arg(args, struct zfs_devdesc *); va_end(args); if (dev->pool_guid == 0) spa = STAILQ_FIRST(&zfs_pools); else spa = spa_find_by_guid(dev->pool_guid); if (!spa) return (ENXIO); mount = malloc(sizeof(*mount)); rv = zfs_mount(spa, dev->root_guid, mount); if (rv != 0) { free(mount); return (rv); } if (mount->objset.os_type != DMU_OST_ZFS) { printf("Unexpected object set type %ju\n", (uintmax_t)mount->objset.os_type); free(mount); return (EIO); } f->f_devdata = mount; free(dev); return (0); } static int zfs_dev_close(struct open_file *f) { free(f->f_devdata); f->f_devdata = NULL; return (0); } static int zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { return (ENOSYS); } struct devsw zfs_dev = { .dv_name = "zfs", .dv_type = DEVT_ZFS, .dv_init = zfs_dev_init, .dv_strategy = zfs_dev_strategy, .dv_open = zfs_dev_open, .dv_close = zfs_dev_close, .dv_ioctl = noioctl, .dv_print = zfs_dev_print, .dv_cleanup = NULL }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) { static char rootname[ZFS_MAXNAMELEN]; static char poolname[ZFS_MAXNAMELEN]; spa_t *spa; const char *end; const char *np; const char *sep; int rv; np = devspec; if (*np != ':') return (EINVAL); np++; end = strchr(np, ':'); if (end == NULL) return (EINVAL); sep = strchr(np, '/'); if (sep == NULL || sep >= end) sep = end; memcpy(poolname, np, sep - np); poolname[sep - np] = '\0'; if (sep < end) { sep++; memcpy(rootname, sep, end - sep); rootname[end - sep] = '\0'; } else rootname[0] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); dev->pool_guid = spa->spa_guid; rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); if (rv != 0) return (rv); if (path != NULL) *path = (*end == '\0') ? end : end + 1; dev->d_dev = &zfs_dev; dev->d_type = zfs_dev.dv_type; return (0); } char * zfs_fmtdev(void *vdev) { static char rootname[ZFS_MAXNAMELEN]; static char buf[2 * ZFS_MAXNAMELEN + 8]; struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; spa_t *spa; buf[0] = '\0'; if (dev->d_type != DEVT_ZFS) return (buf); if (dev->pool_guid == 0) { spa = STAILQ_FIRST(&zfs_pools); dev->pool_guid = spa->spa_guid; } else spa = spa_find_by_guid(dev->pool_guid); if (spa == NULL) { printf("ZFS: can't find pool by guid\n"); return (buf); } if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { printf("ZFS: can't find root filesystem\n"); return (buf); } if (zfs_rlookup(spa, dev->root_guid, rootname)) { printf("ZFS: can't find filesystem by guid\n"); return (buf); } if (rootname[0] == '\0') sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name); else sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name, rootname); return (buf); } int zfs_list(const char *name) { static char poolname[ZFS_MAXNAMELEN]; uint64_t objid; spa_t *spa; const char *dsname; int len; int rv; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; memcpy(poolname, name, len); poolname[len] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return (rv); - rv = zfs_list_dataset(spa, objid); + + return (zfs_list_dataset(spa, objid)); +} + +int +zfs_bootenv(const char *name) +{ + static char poolname[ZFS_MAXNAMELEN], *dsname; + char becount[4]; + uint64_t objid; + spa_t *spa; + int len, rv, pages, perpage, currpage; + + if (strcmp(name, getenv("zfs_be_root")) != 0) { + if (setenv("zfs_be_root", name, 1) != 0) + return (ENOMEM); + } + + SLIST_INIT(&zfs_be_head); + zfs_env_count = 0; + len = strlen(name); + dsname = strchr(name, '/'); + if (dsname != NULL) { + len = dsname - name; + dsname++; + } else + dsname = ""; + memcpy(poolname, name, len); + poolname[len] = '\0'; + + spa = spa_find_by_name(poolname); + if (!spa) + return (ENXIO); + rv = zfs_lookup_dataset(spa, dsname, &objid); + if (rv != 0) + return (rv); + rv = zfs_callback_dataset(spa, objid, zfs_belist_add); + + /* Calculate and store the number of pages of BEs */ + perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); + pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); + snprintf(becount, 4, "%d", pages); + if (setenv("zfs_be_pages", becount, 1) != 0) + return (ENOMEM); + + /* Roll over the page counter if it has exceeded the maximum */ + currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); + if (currpage > pages) { + if (setenv("zfs_be_currpage", "1", 1) != 0) + return (ENOMEM); + } + + /* Populate the menu environment variables */ + zfs_set_env(); + + /* Clean up the SLIST of ZFS BEs */ + while (!SLIST_EMPTY(&zfs_be_head)) { + zfs_be = SLIST_FIRST(&zfs_be_head); + SLIST_REMOVE_HEAD(&zfs_be_head, entries); + free(zfs_be); + } + + return (rv); +} + +int +zfs_belist_add(const char *name) +{ + + /* Add the boot environment to the head of the SLIST */ + zfs_be = malloc(sizeof(struct zfs_be_entry)); + zfs_be->name = name; + SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); + zfs_env_count++; + + return (0); +} + +int +zfs_set_env(void) +{ + char envname[32], envval[256]; + char *beroot, *pagenum; + int rv, page, ctr; + + beroot = getenv("zfs_be_root"); + if (beroot == NULL) { + return (1); + } + + pagenum = getenv("zfs_be_currpage"); + if (pagenum != NULL) { + page = strtol(pagenum, NULL, 10); + } else { + page = 1; + } + + ctr = 1; + rv = 0; + zfs_env_index = ZFS_BE_FIRST; + SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { + /* Skip to the requested page number */ + if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { + ctr++; + continue; + } + + snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); + snprintf(envval, sizeof(envval), "%s", zfs_be->name); + rv = setenv(envname, envval, 1); + if (rv != 0) { + break; + } + + snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); + rv = setenv(envname, envval, 1); + if (rv != 0){ + break; + } + + snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); + rv = setenv(envname, "set_bootenv", 1); + if (rv != 0){ + break; + } + + snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); + snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); + rv = setenv(envname, envval, 1); + if (rv != 0){ + break; + } + + zfs_env_index++; + if (zfs_env_index > ZFS_BE_LAST) { + break; + } + + } + + for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { + snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); + (void)unsetenv(envname); + snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); + (void)unsetenv(envname); + snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); + (void)unsetenv(envname); + snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); + (void)unsetenv(envname); + } + return (rv); -} +} \ No newline at end of file Index: projects/clang380-import/sys/boot/zfs/zfsimpl.c =================================================================== --- projects/clang380-import/sys/boot/zfs/zfsimpl.c (revision 293015) +++ projects/clang380-import/sys/boot/zfs/zfsimpl.c (revision 293016) @@ -1,2143 +1,2193 @@ /*- * Copyright (c) 2007 Doug Rabson * 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. */ #include __FBSDID("$FreeBSD$"); /* * Stand-alone ZFS file reader. */ #include #include #include "zfsimpl.h" #include "zfssubr.c" struct zfsmount { const spa_t *spa; objset_phys_t objset; uint64_t rootobj; }; /* * List of all vdevs, chained through v_alllink. */ static vdev_list_t zfs_vdevs; /* * List of ZFS features supported for read */ static const char *features_for_read[] = { "org.illumos:lz4_compress", "com.delphix:hole_birth", "com.delphix:extensible_dataset", "com.delphix:embedded_data", "org.open-zfs:large_blocks", NULL }; /* * List of all pools, chained through spa_link. */ static spa_list_t zfs_pools; static uint64_t zfs_crc64_table[256]; static const dnode_phys_t *dnode_cache_obj = 0; static uint64_t dnode_cache_bn; static char *dnode_cache_buf; static char *zap_scratch; static char *zfs_temp_buf, *zfs_temp_end, *zfs_temp_ptr; #define TEMP_SIZE (1024 * 1024) static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf); static int zfs_get_root(const spa_t *spa, uint64_t *objid); static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result); static void zfs_init(void) { STAILQ_INIT(&zfs_vdevs); STAILQ_INIT(&zfs_pools); zfs_temp_buf = malloc(TEMP_SIZE); zfs_temp_end = zfs_temp_buf + TEMP_SIZE; zfs_temp_ptr = zfs_temp_buf; dnode_cache_buf = malloc(SPA_MAXBLOCKSIZE); zap_scratch = malloc(SPA_MAXBLOCKSIZE); zfs_init_crc(); } static void * zfs_alloc(size_t size) { char *ptr; if (zfs_temp_ptr + size > zfs_temp_end) { printf("ZFS: out of temporary buffer space\n"); for (;;) ; } ptr = zfs_temp_ptr; zfs_temp_ptr += size; return (ptr); } static void zfs_free(void *ptr, size_t size) { zfs_temp_ptr -= size; if (zfs_temp_ptr != ptr) { printf("ZFS: zfs_alloc()/zfs_free() mismatch\n"); for (;;) ; } } static int xdr_int(const unsigned char **xdr, int *ip) { *ip = ((*xdr)[0] << 24) | ((*xdr)[1] << 16) | ((*xdr)[2] << 8) | ((*xdr)[3] << 0); (*xdr) += 4; return (0); } static int xdr_u_int(const unsigned char **xdr, u_int *ip) { *ip = ((*xdr)[0] << 24) | ((*xdr)[1] << 16) | ((*xdr)[2] << 8) | ((*xdr)[3] << 0); (*xdr) += 4; return (0); } static int xdr_uint64_t(const unsigned char **xdr, uint64_t *lp) { u_int hi, lo; xdr_u_int(xdr, &hi); xdr_u_int(xdr, &lo); *lp = (((uint64_t) hi) << 32) | lo; return (0); } static int nvlist_find(const unsigned char *nvlist, const char *name, int type, int* elementsp, void *valuep) { const unsigned char *p, *pair; int junk; int encoded_size, decoded_size; p = nvlist; xdr_int(&p, &junk); xdr_int(&p, &junk); pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); while (encoded_size && decoded_size) { int namelen, pairtype, elements; const char *pairname; xdr_int(&p, &namelen); pairname = (const char*) p; p += roundup(namelen, 4); xdr_int(&p, &pairtype); if (!memcmp(name, pairname, namelen) && type == pairtype) { xdr_int(&p, &elements); if (elementsp) *elementsp = elements; if (type == DATA_TYPE_UINT64) { xdr_uint64_t(&p, (uint64_t *) valuep); return (0); } else if (type == DATA_TYPE_STRING) { int len; xdr_int(&p, &len); (*(const char**) valuep) = (const char*) p; return (0); } else if (type == DATA_TYPE_NVLIST || type == DATA_TYPE_NVLIST_ARRAY) { (*(const unsigned char**) valuep) = (const unsigned char*) p; return (0); } else { return (EIO); } } else { /* * Not the pair we are looking for, skip to the next one. */ p = pair + encoded_size; } pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); } return (EIO); } static int nvlist_check_features_for_read(const unsigned char *nvlist) { const unsigned char *p, *pair; int junk; int encoded_size, decoded_size; int rc; rc = 0; p = nvlist; xdr_int(&p, &junk); xdr_int(&p, &junk); pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); while (encoded_size && decoded_size) { int namelen, pairtype; const char *pairname; int i, found; found = 0; xdr_int(&p, &namelen); pairname = (const char*) p; p += roundup(namelen, 4); xdr_int(&p, &pairtype); for (i = 0; features_for_read[i] != NULL; i++) { if (!memcmp(pairname, features_for_read[i], namelen)) { found = 1; break; } } if (!found) { printf("ZFS: unsupported feature: %s\n", pairname); rc = EIO; } p = pair + encoded_size; pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); } return (rc); } /* * Return the next nvlist in an nvlist array. */ static const unsigned char * nvlist_next(const unsigned char *nvlist) { const unsigned char *p, *pair; int junk; int encoded_size, decoded_size; p = nvlist; xdr_int(&p, &junk); xdr_int(&p, &junk); pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); while (encoded_size && decoded_size) { p = pair + encoded_size; pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); } return p; } #ifdef TEST static const unsigned char * nvlist_print(const unsigned char *nvlist, unsigned int indent) { static const char* typenames[] = { "DATA_TYPE_UNKNOWN", "DATA_TYPE_BOOLEAN", "DATA_TYPE_BYTE", "DATA_TYPE_INT16", "DATA_TYPE_UINT16", "DATA_TYPE_INT32", "DATA_TYPE_UINT32", "DATA_TYPE_INT64", "DATA_TYPE_UINT64", "DATA_TYPE_STRING", "DATA_TYPE_BYTE_ARRAY", "DATA_TYPE_INT16_ARRAY", "DATA_TYPE_UINT16_ARRAY", "DATA_TYPE_INT32_ARRAY", "DATA_TYPE_UINT32_ARRAY", "DATA_TYPE_INT64_ARRAY", "DATA_TYPE_UINT64_ARRAY", "DATA_TYPE_STRING_ARRAY", "DATA_TYPE_HRTIME", "DATA_TYPE_NVLIST", "DATA_TYPE_NVLIST_ARRAY", "DATA_TYPE_BOOLEAN_VALUE", "DATA_TYPE_INT8", "DATA_TYPE_UINT8", "DATA_TYPE_BOOLEAN_ARRAY", "DATA_TYPE_INT8_ARRAY", "DATA_TYPE_UINT8_ARRAY" }; unsigned int i, j; const unsigned char *p, *pair; int junk; int encoded_size, decoded_size; p = nvlist; xdr_int(&p, &junk); xdr_int(&p, &junk); pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); while (encoded_size && decoded_size) { int namelen, pairtype, elements; const char *pairname; xdr_int(&p, &namelen); pairname = (const char*) p; p += roundup(namelen, 4); xdr_int(&p, &pairtype); for (i = 0; i < indent; i++) printf(" "); printf("%s %s", typenames[pairtype], pairname); xdr_int(&p, &elements); switch (pairtype) { case DATA_TYPE_UINT64: { uint64_t val; xdr_uint64_t(&p, &val); printf(" = 0x%jx\n", (uintmax_t)val); break; } case DATA_TYPE_STRING: { int len; xdr_int(&p, &len); printf(" = \"%s\"\n", p); break; } case DATA_TYPE_NVLIST: printf("\n"); nvlist_print(p, indent + 1); break; case DATA_TYPE_NVLIST_ARRAY: for (j = 0; j < elements; j++) { printf("[%d]\n", j); p = nvlist_print(p, indent + 1); if (j != elements - 1) { for (i = 0; i < indent; i++) printf(" "); printf("%s %s", typenames[pairtype], pairname); } } break; default: printf("\n"); } p = pair + encoded_size; pair = p; xdr_int(&p, &encoded_size); xdr_int(&p, &decoded_size); } return p; } #endif static int vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset, size_t size) { size_t psize; int rc; if (!vdev->v_phys_read) return (EIO); if (bp) { psize = BP_GET_PSIZE(bp); } else { psize = size; } /*printf("ZFS: reading %d bytes at 0x%jx to %p\n", psize, (uintmax_t)offset, buf);*/ rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize); if (rc) return (rc); if (bp && zio_checksum_verify(bp, buf)) return (EIO); return (0); } static int vdev_disk_read(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset, size_t bytes) { return (vdev_read_phys(vdev, bp, buf, offset + VDEV_LABEL_START_SIZE, bytes)); } static int vdev_mirror_read(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset, size_t bytes) { vdev_t *kid; int rc; rc = EIO; STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { if (kid->v_state != VDEV_STATE_HEALTHY) continue; rc = kid->v_read(kid, bp, buf, offset, bytes); if (!rc) return (0); } return (rc); } static int vdev_replacing_read(vdev_t *vdev, const blkptr_t *bp, void *buf, off_t offset, size_t bytes) { vdev_t *kid; /* * Here we should have two kids: * First one which is the one we are replacing and we can trust * only this one to have valid data, but it might not be present. * Second one is that one we are replacing with. It is most likely * healthy, but we can't trust it has needed data, so we won't use it. */ kid = STAILQ_FIRST(&vdev->v_children); if (kid == NULL) return (EIO); if (kid->v_state != VDEV_STATE_HEALTHY) return (EIO); return (kid->v_read(kid, bp, buf, offset, bytes)); } static vdev_t * vdev_find(uint64_t guid) { vdev_t *vdev; STAILQ_FOREACH(vdev, &zfs_vdevs, v_alllink) if (vdev->v_guid == guid) return (vdev); return (0); } static vdev_t * vdev_create(uint64_t guid, vdev_read_t *read) { vdev_t *vdev; vdev = malloc(sizeof(vdev_t)); memset(vdev, 0, sizeof(vdev_t)); STAILQ_INIT(&vdev->v_children); vdev->v_guid = guid; vdev->v_state = VDEV_STATE_OFFLINE; vdev->v_read = read; vdev->v_phys_read = 0; vdev->v_read_priv = 0; STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink); return (vdev); } static int vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, vdev_t **vdevp, int is_newer) { int rc; uint64_t guid, id, ashift, nparity; const char *type; const char *path; vdev_t *vdev, *kid; const unsigned char *kids; int nkids, i, is_new; uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present; if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, 0, &guid) || nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, 0, &id) || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING, 0, &type)) { printf("ZFS: can't find vdev details\n"); return (ENOENT); } if (strcmp(type, VDEV_TYPE_MIRROR) && strcmp(type, VDEV_TYPE_DISK) #ifdef ZFS_TEST && strcmp(type, VDEV_TYPE_FILE) #endif && strcmp(type, VDEV_TYPE_RAIDZ) && strcmp(type, VDEV_TYPE_REPLACING)) { printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n"); return (EIO); } is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0; nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, 0, &is_offline); nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, 0, &is_removed); nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, 0, &is_faulted); nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, 0, &is_degraded); nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, 0, &isnt_present); vdev = vdev_find(guid); if (!vdev) { is_new = 1; if (!strcmp(type, VDEV_TYPE_MIRROR)) vdev = vdev_create(guid, vdev_mirror_read); else if (!strcmp(type, VDEV_TYPE_RAIDZ)) vdev = vdev_create(guid, vdev_raidz_read); else if (!strcmp(type, VDEV_TYPE_REPLACING)) vdev = vdev_create(guid, vdev_replacing_read); else vdev = vdev_create(guid, vdev_disk_read); vdev->v_id = id; vdev->v_top = pvdev != NULL ? pvdev : vdev; if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT, DATA_TYPE_UINT64, 0, &ashift) == 0) vdev->v_ashift = ashift; else vdev->v_ashift = 0; if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY, DATA_TYPE_UINT64, 0, &nparity) == 0) vdev->v_nparity = nparity; else vdev->v_nparity = 0; if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH, DATA_TYPE_STRING, 0, &path) == 0) { if (strncmp(path, "/dev/", 5) == 0) path += 5; vdev->v_name = strdup(path); } else { if (!strcmp(type, "raidz")) { if (vdev->v_nparity == 1) vdev->v_name = "raidz1"; else if (vdev->v_nparity == 2) vdev->v_name = "raidz2"; else if (vdev->v_nparity == 3) vdev->v_name = "raidz3"; else { printf("ZFS: can only boot from disk, mirror, raidz1, raidz2 and raidz3 vdevs\n"); return (EIO); } } else { vdev->v_name = strdup(type); } } } else { is_new = 0; } if (is_new || is_newer) { /* * This is either new vdev or we've already seen this vdev, * but from an older vdev label, so let's refresh its state * from the newer label. */ if (is_offline) vdev->v_state = VDEV_STATE_OFFLINE; else if (is_removed) vdev->v_state = VDEV_STATE_REMOVED; else if (is_faulted) vdev->v_state = VDEV_STATE_FAULTED; else if (is_degraded) vdev->v_state = VDEV_STATE_DEGRADED; else if (isnt_present) vdev->v_state = VDEV_STATE_CANT_OPEN; } rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, &nkids, &kids); /* * Its ok if we don't have any kids. */ if (rc == 0) { vdev->v_nchildren = nkids; for (i = 0; i < nkids; i++) { rc = vdev_init_from_nvlist(kids, vdev, &kid, is_newer); if (rc) return (rc); if (is_new) STAILQ_INSERT_TAIL(&vdev->v_children, kid, v_childlink); kids = nvlist_next(kids); } } else { vdev->v_nchildren = 0; } if (vdevp) *vdevp = vdev; return (0); } static void vdev_set_state(vdev_t *vdev) { vdev_t *kid; int good_kids; int bad_kids; /* * A mirror or raidz is healthy if all its kids are healthy. A * mirror is degraded if any of its kids is healthy; a raidz * is degraded if at most nparity kids are offline. */ if (STAILQ_FIRST(&vdev->v_children)) { good_kids = 0; bad_kids = 0; STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { if (kid->v_state == VDEV_STATE_HEALTHY) good_kids++; else bad_kids++; } if (bad_kids == 0) { vdev->v_state = VDEV_STATE_HEALTHY; } else { if (vdev->v_read == vdev_mirror_read) { if (good_kids) { vdev->v_state = VDEV_STATE_DEGRADED; } else { vdev->v_state = VDEV_STATE_OFFLINE; } } else if (vdev->v_read == vdev_raidz_read) { if (bad_kids > vdev->v_nparity) { vdev->v_state = VDEV_STATE_OFFLINE; } else { vdev->v_state = VDEV_STATE_DEGRADED; } } } } } static spa_t * spa_find_by_guid(uint64_t guid) { spa_t *spa; STAILQ_FOREACH(spa, &zfs_pools, spa_link) if (spa->spa_guid == guid) return (spa); return (0); } static spa_t * spa_find_by_name(const char *name) { spa_t *spa; STAILQ_FOREACH(spa, &zfs_pools, spa_link) if (!strcmp(spa->spa_name, name)) return (spa); return (0); } #ifdef BOOT2 static spa_t * spa_get_primary(void) { return (STAILQ_FIRST(&zfs_pools)); } static vdev_t * spa_get_primary_vdev(const spa_t *spa) { vdev_t *vdev; vdev_t *kid; if (spa == NULL) spa = spa_get_primary(); if (spa == NULL) return (NULL); vdev = STAILQ_FIRST(&spa->spa_vdevs); if (vdev == NULL) return (NULL); for (kid = STAILQ_FIRST(&vdev->v_children); kid != NULL; kid = STAILQ_FIRST(&vdev->v_children)) vdev = kid; return (vdev); } #endif static spa_t * spa_create(uint64_t guid) { spa_t *spa; spa = malloc(sizeof(spa_t)); memset(spa, 0, sizeof(spa_t)); STAILQ_INIT(&spa->spa_vdevs); spa->spa_guid = guid; STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link); return (spa); } static const char * state_name(vdev_state_t state) { static const char* names[] = { "UNKNOWN", "CLOSED", "OFFLINE", "REMOVED", "CANT_OPEN", "FAULTED", "DEGRADED", "ONLINE" }; return names[state]; } #ifdef BOOT2 #define pager_printf printf #else static void pager_printf(const char *fmt, ...) { char line[80]; va_list args; va_start(args, fmt); vsprintf(line, fmt, args); va_end(args); pager_output(line); } #endif #define STATUS_FORMAT " %s %s\n" static void print_state(int indent, const char *name, vdev_state_t state) { int i; char buf[512]; buf[0] = 0; for (i = 0; i < indent; i++) strcat(buf, " "); strcat(buf, name); pager_printf(STATUS_FORMAT, buf, state_name(state)); } static void vdev_status(vdev_t *vdev, int indent) { vdev_t *kid; print_state(indent, vdev->v_name, vdev->v_state); STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { vdev_status(kid, indent + 1); } } static void spa_status(spa_t *spa) { static char bootfs[ZFS_MAXNAMELEN]; uint64_t rootid; vdev_t *vdev; int good_kids, bad_kids, degraded_kids; vdev_state_t state; pager_printf(" pool: %s\n", spa->spa_name); if (zfs_get_root(spa, &rootid) == 0 && zfs_rlookup(spa, rootid, bootfs) == 0) { if (bootfs[0] == '\0') pager_printf("bootfs: %s\n", spa->spa_name); else pager_printf("bootfs: %s/%s\n", spa->spa_name, bootfs); } pager_printf("config:\n\n"); pager_printf(STATUS_FORMAT, "NAME", "STATE"); good_kids = 0; degraded_kids = 0; bad_kids = 0; STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) { if (vdev->v_state == VDEV_STATE_HEALTHY) good_kids++; else if (vdev->v_state == VDEV_STATE_DEGRADED) degraded_kids++; else bad_kids++; } state = VDEV_STATE_CLOSED; if (good_kids > 0 && (degraded_kids + bad_kids) == 0) state = VDEV_STATE_HEALTHY; else if ((good_kids + degraded_kids) > 0) state = VDEV_STATE_DEGRADED; print_state(0, spa->spa_name, state); STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) { vdev_status(vdev, 1); } } static void spa_all_status(void) { spa_t *spa; int first = 1; STAILQ_FOREACH(spa, &zfs_pools, spa_link) { if (!first) pager_printf("\n"); first = 0; spa_status(spa); } } static int vdev_probe(vdev_phys_read_t *read, void *read_priv, spa_t **spap) { vdev_t vtmp; vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch; spa_t *spa; vdev_t *vdev, *top_vdev, *pool_vdev; off_t off; blkptr_t bp; const unsigned char *nvlist; uint64_t val; uint64_t guid; uint64_t pool_txg, pool_guid; uint64_t is_log; const char *pool_name; const unsigned char *vdevs; const unsigned char *features; int i, rc, is_newer; char *upbuf; const struct uberblock *up; /* * Load the vdev label and figure out which * uberblock is most current. */ memset(&vtmp, 0, sizeof(vtmp)); vtmp.v_phys_read = read; vtmp.v_read_priv = read_priv; off = offsetof(vdev_label_t, vl_vdev_phys); BP_ZERO(&bp); BP_SET_LSIZE(&bp, sizeof(vdev_phys_t)); BP_SET_PSIZE(&bp, sizeof(vdev_phys_t)); BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); DVA_SET_OFFSET(BP_IDENTITY(&bp), off); ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); if (vdev_read_phys(&vtmp, &bp, vdev_label, off, 0)) return (EIO); if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) { return (EIO); } nvlist = (const unsigned char *) vdev_label->vp_nvlist + 4; if (nvlist_find(nvlist, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64, 0, &val)) { return (EIO); } if (!SPA_VERSION_IS_SUPPORTED(val)) { printf("ZFS: unsupported ZFS version %u (should be %u)\n", (unsigned) val, (unsigned) SPA_VERSION); return (EIO); } /* Check ZFS features for read */ if (nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ, DATA_TYPE_NVLIST, 0, &features) == 0 && nvlist_check_features_for_read(features) != 0) return (EIO); if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64, 0, &val)) { return (EIO); } if (val == POOL_STATE_DESTROYED) { /* We don't boot only from destroyed pools. */ return (EIO); } if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64, 0, &pool_txg) || nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, 0, &pool_guid) || nvlist_find(nvlist, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING, 0, &pool_name)) { /* * Cache and spare devices end up here - just ignore * them. */ /*printf("ZFS: can't find pool details\n");*/ return (EIO); } is_log = 0; (void) nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, 0, &is_log); if (is_log) return (EIO); /* * Create the pool if this is the first time we've seen it. */ spa = spa_find_by_guid(pool_guid); if (!spa) { spa = spa_create(pool_guid); spa->spa_name = strdup(pool_name); } if (pool_txg > spa->spa_txg) { spa->spa_txg = pool_txg; is_newer = 1; } else is_newer = 0; /* * Get the vdev tree and create our in-core copy of it. * If we already have a vdev with this guid, this must * be some kind of alias (overlapping slices, dangerously dedicated * disks etc). */ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, 0, &guid)) { return (EIO); } vdev = vdev_find(guid); if (vdev && vdev->v_phys_read) /* Has this vdev already been inited? */ return (EIO); if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, 0, &vdevs)) { return (EIO); } rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer); if (rc) return (rc); /* * Add the toplevel vdev to the pool if its not already there. */ STAILQ_FOREACH(pool_vdev, &spa->spa_vdevs, v_childlink) if (top_vdev == pool_vdev) break; if (!pool_vdev && top_vdev) STAILQ_INSERT_TAIL(&spa->spa_vdevs, top_vdev, v_childlink); /* * We should already have created an incomplete vdev for this * vdev. Find it and initialise it with our read proc. */ vdev = vdev_find(guid); if (vdev) { vdev->v_phys_read = read; vdev->v_read_priv = read_priv; vdev->v_state = VDEV_STATE_HEALTHY; } else { printf("ZFS: inconsistent nvlist contents\n"); return (EIO); } /* * Re-evaluate top-level vdev state. */ vdev_set_state(top_vdev); /* * Ok, we are happy with the pool so far. Lets find * the best uberblock and then we can actually access * the contents of the pool. */ upbuf = zfs_alloc(VDEV_UBERBLOCK_SIZE(vdev)); up = (const struct uberblock *)upbuf; for (i = 0; i < VDEV_UBERBLOCK_COUNT(vdev); i++) { off = VDEV_UBERBLOCK_OFFSET(vdev, i); BP_ZERO(&bp); DVA_SET_OFFSET(&bp.blk_dva[0], off); BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); if (vdev_read_phys(vdev, &bp, upbuf, off, 0)) continue; if (up->ub_magic != UBERBLOCK_MAGIC) continue; if (up->ub_txg < spa->spa_txg) continue; if (up->ub_txg > spa->spa_uberblock.ub_txg) { spa->spa_uberblock = *up; } else if (up->ub_txg == spa->spa_uberblock.ub_txg) { if (up->ub_timestamp > spa->spa_uberblock.ub_timestamp) spa->spa_uberblock = *up; } } zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev)); if (spap) *spap = spa; return (0); } static int ilog2(int n) { int v; for (v = 0; v < 32; v++) if (n == (1 << v)) return v; return -1; } static int zio_read_gang(const spa_t *spa, const blkptr_t *bp, void *buf) { blkptr_t gbh_bp; zio_gbh_phys_t zio_gb; char *pbuf; int i; /* Artificial BP for gang block header. */ gbh_bp = *bp; BP_SET_PSIZE(&gbh_bp, SPA_GANGBLOCKSIZE); BP_SET_LSIZE(&gbh_bp, SPA_GANGBLOCKSIZE); BP_SET_CHECKSUM(&gbh_bp, ZIO_CHECKSUM_GANG_HEADER); BP_SET_COMPRESS(&gbh_bp, ZIO_COMPRESS_OFF); for (i = 0; i < SPA_DVAS_PER_BP; i++) DVA_SET_GANG(&gbh_bp.blk_dva[i], 0); /* Read gang header block using the artificial BP. */ if (zio_read(spa, &gbh_bp, &zio_gb)) return (EIO); pbuf = buf; for (i = 0; i < SPA_GBH_NBLKPTRS; i++) { blkptr_t *gbp = &zio_gb.zg_blkptr[i]; if (BP_IS_HOLE(gbp)) continue; if (zio_read(spa, gbp, pbuf)) return (EIO); pbuf += BP_GET_PSIZE(gbp); } if (zio_checksum_verify(bp, buf)) return (EIO); return (0); } static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf) { int cpfunc = BP_GET_COMPRESS(bp); uint64_t align, size; void *pbuf; int i, error; /* * Process data embedded in block pointer */ if (BP_IS_EMBEDDED(bp)) { ASSERT(BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA); size = BPE_GET_PSIZE(bp); ASSERT(size <= BPE_PAYLOAD_SIZE); if (cpfunc != ZIO_COMPRESS_OFF) pbuf = zfs_alloc(size); else pbuf = buf; decode_embedded_bp_compressed(bp, pbuf); error = 0; if (cpfunc != ZIO_COMPRESS_OFF) { error = zio_decompress_data(cpfunc, pbuf, size, buf, BP_GET_LSIZE(bp)); zfs_free(pbuf, size); } if (error != 0) printf("ZFS: i/o error - unable to decompress block pointer data, error %d\n", error); return (error); } error = EIO; for (i = 0; i < SPA_DVAS_PER_BP; i++) { const dva_t *dva = &bp->blk_dva[i]; vdev_t *vdev; int vdevid; off_t offset; if (!dva->dva_word[0] && !dva->dva_word[1]) continue; vdevid = DVA_GET_VDEV(dva); offset = DVA_GET_OFFSET(dva); STAILQ_FOREACH(vdev, &spa->spa_vdevs, v_childlink) { if (vdev->v_id == vdevid) break; } if (!vdev || !vdev->v_read) continue; size = BP_GET_PSIZE(bp); if (vdev->v_read == vdev_raidz_read) { align = 1ULL << vdev->v_top->v_ashift; if (P2PHASE(size, align) != 0) size = P2ROUNDUP(size, align); } if (size != BP_GET_PSIZE(bp) || cpfunc != ZIO_COMPRESS_OFF) pbuf = zfs_alloc(size); else pbuf = buf; if (DVA_GET_GANG(dva)) error = zio_read_gang(spa, bp, pbuf); else error = vdev->v_read(vdev, bp, pbuf, offset, size); if (error == 0) { if (cpfunc != ZIO_COMPRESS_OFF) error = zio_decompress_data(cpfunc, pbuf, BP_GET_PSIZE(bp), buf, BP_GET_LSIZE(bp)); else if (size != BP_GET_PSIZE(bp)) bcopy(pbuf, buf, BP_GET_PSIZE(bp)); } if (buf != pbuf) zfs_free(pbuf, size); if (error == 0) break; } if (error != 0) printf("ZFS: i/o error - all block copies unavailable\n"); return (error); } static int dnode_read(const spa_t *spa, const dnode_phys_t *dnode, off_t offset, void *buf, size_t buflen) { int ibshift = dnode->dn_indblkshift - SPA_BLKPTRSHIFT; int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; int nlevels = dnode->dn_nlevels; int i, rc; if (bsize > SPA_MAXBLOCKSIZE) { printf("ZFS: I/O error - blocks larger than 128K are not supported\n"); return (EIO); } /* * Note: bsize may not be a power of two here so we need to do an * actual divide rather than a bitshift. */ while (buflen > 0) { uint64_t bn = offset / bsize; int boff = offset % bsize; int ibn; const blkptr_t *indbp; blkptr_t bp; if (bn > dnode->dn_maxblkid) return (EIO); if (dnode == dnode_cache_obj && bn == dnode_cache_bn) goto cached; indbp = dnode->dn_blkptr; for (i = 0; i < nlevels; i++) { /* * Copy the bp from the indirect array so that * we can re-use the scratch buffer for multi-level * objects. */ ibn = bn >> ((nlevels - i - 1) * ibshift); ibn &= ((1 << ibshift) - 1); bp = indbp[ibn]; if (BP_IS_HOLE(&bp)) { memset(dnode_cache_buf, 0, bsize); break; } rc = zio_read(spa, &bp, dnode_cache_buf); if (rc) return (rc); indbp = (const blkptr_t *) dnode_cache_buf; } dnode_cache_obj = dnode; dnode_cache_bn = bn; cached: /* * The buffer contains our data block. Copy what we * need from it and loop. */ i = bsize - boff; if (i > buflen) i = buflen; memcpy(buf, &dnode_cache_buf[boff], i); buf = ((char*) buf) + i; offset += i; buflen -= i; } return (0); } /* * Lookup a value in a microzap directory. Assumes that the zap * scratch buffer contains the directory contents. */ static int mzap_lookup(const dnode_phys_t *dnode, const char *name, uint64_t *value) { const mzap_phys_t *mz; const mzap_ent_phys_t *mze; size_t size; int chunks, i; /* * Microzap objects use exactly one block. Read the whole * thing. */ size = dnode->dn_datablkszsec * 512; mz = (const mzap_phys_t *) zap_scratch; chunks = size / MZAP_ENT_LEN - 1; for (i = 0; i < chunks; i++) { mze = &mz->mz_chunk[i]; if (!strcmp(mze->mze_name, name)) { *value = mze->mze_value; return (0); } } return (ENOENT); } /* * Compare a name with a zap leaf entry. Return non-zero if the name * matches. */ static int fzap_name_equal(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, const char *name) { size_t namelen; const zap_leaf_chunk_t *nc; const char *p; namelen = zc->l_entry.le_name_numints; nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk); p = name; while (namelen > 0) { size_t len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; if (memcmp(p, nc->l_array.la_array, len)) return (0); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next); } return 1; } /* * Extract a uint64_t value from a zap leaf entry. */ static uint64_t fzap_leaf_value(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc) { const zap_leaf_chunk_t *vc; int i; uint64_t value; const uint8_t *p; vc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_value_chunk); for (i = 0, value = 0, p = vc->l_array.la_array; i < 8; i++) { value = (value << 8) | p[i]; } return value; } /* * Lookup a value in a fatzap directory. Assumes that the zap scratch * buffer contains the directory header. */ static int fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value) { int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; zap_phys_t zh = *(zap_phys_t *) zap_scratch; fat_zap_t z; uint64_t *ptrtbl; uint64_t hash; int rc; if (zh.zap_magic != ZAP_MAGIC) return (EIO); z.zap_block_shift = ilog2(bsize); z.zap_phys = (zap_phys_t *) zap_scratch; /* * Figure out where the pointer table is and read it in if necessary. */ if (zh.zap_ptrtbl.zt_blk) { rc = dnode_read(spa, dnode, zh.zap_ptrtbl.zt_blk * bsize, zap_scratch, bsize); if (rc) return (rc); ptrtbl = (uint64_t *) zap_scratch; } else { ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0); } hash = zap_hash(zh.zap_salt, name); zap_leaf_t zl; zl.l_bs = z.zap_block_shift; off_t off = ptrtbl[hash >> (64 - zh.zap_ptrtbl.zt_shift)] << zl.l_bs; zap_leaf_chunk_t *zc; rc = dnode_read(spa, dnode, off, zap_scratch, bsize); if (rc) return (rc); zl.l_phys = (zap_leaf_phys_t *) zap_scratch; /* * Make sure this chunk matches our hash. */ if (zl.l_phys->l_hdr.lh_prefix_len > 0 && zl.l_phys->l_hdr.lh_prefix != hash >> (64 - zl.l_phys->l_hdr.lh_prefix_len)) return (ENOENT); /* * Hash within the chunk to find our entry. */ int shift = (64 - ZAP_LEAF_HASH_SHIFT(&zl) - zl.l_phys->l_hdr.lh_prefix_len); int h = (hash >> shift) & ((1 << ZAP_LEAF_HASH_SHIFT(&zl)) - 1); h = zl.l_phys->l_hash[h]; if (h == 0xffff) return (ENOENT); zc = &ZAP_LEAF_CHUNK(&zl, h); while (zc->l_entry.le_hash != hash) { if (zc->l_entry.le_next == 0xffff) { zc = 0; break; } zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next); } if (fzap_name_equal(&zl, zc, name)) { if (zc->l_entry.le_value_intlen * zc->l_entry.le_value_numints > 8) return (E2BIG); *value = fzap_leaf_value(&zl, zc); return (0); } return (ENOENT); } /* * Lookup a name in a zap object and return its value as a uint64_t. */ static int zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name, uint64_t *value) { int rc; uint64_t zap_type; size_t size = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; rc = dnode_read(spa, dnode, 0, zap_scratch, size); if (rc) return (rc); zap_type = *(uint64_t *) zap_scratch; if (zap_type == ZBT_MICRO) return mzap_lookup(dnode, name, value); else if (zap_type == ZBT_HEADER) return fzap_lookup(spa, dnode, name, value); printf("ZFS: invalid zap_type=%d\n", (int)zap_type); return (EIO); } /* * List a microzap directory. Assumes that the zap scratch buffer contains * the directory contents. */ static int -mzap_list(const dnode_phys_t *dnode) +mzap_list(const dnode_phys_t *dnode, int (*callback)(const char *)) { const mzap_phys_t *mz; const mzap_ent_phys_t *mze; size_t size; int chunks, i; /* * Microzap objects use exactly one block. Read the whole * thing. */ size = dnode->dn_datablkszsec * 512; mz = (const mzap_phys_t *) zap_scratch; chunks = size / MZAP_ENT_LEN - 1; for (i = 0; i < chunks; i++) { mze = &mz->mz_chunk[i]; if (mze->mze_name[0]) //printf("%-32s 0x%jx\n", mze->mze_name, (uintmax_t)mze->mze_value); - printf("%s\n", mze->mze_name); + callback(mze->mze_name); } return (0); } /* * List a fatzap directory. Assumes that the zap scratch buffer contains * the directory header. */ static int -fzap_list(const spa_t *spa, const dnode_phys_t *dnode) +fzap_list(const spa_t *spa, const dnode_phys_t *dnode, int (*callback)(const char *)) { int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; zap_phys_t zh = *(zap_phys_t *) zap_scratch; fat_zap_t z; int i, j; if (zh.zap_magic != ZAP_MAGIC) return (EIO); z.zap_block_shift = ilog2(bsize); z.zap_phys = (zap_phys_t *) zap_scratch; /* * This assumes that the leaf blocks start at block 1. The * documentation isn't exactly clear on this. */ zap_leaf_t zl; zl.l_bs = z.zap_block_shift; for (i = 0; i < zh.zap_num_leafs; i++) { off_t off = (i + 1) << zl.l_bs; char name[256], *p; uint64_t value; if (dnode_read(spa, dnode, off, zap_scratch, bsize)) return (EIO); zl.l_phys = (zap_leaf_phys_t *) zap_scratch; for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) { zap_leaf_chunk_t *zc, *nc; int namelen; zc = &ZAP_LEAF_CHUNK(&zl, j); if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) continue; namelen = zc->l_entry.le_name_numints; if (namelen > sizeof(name)) namelen = sizeof(name); /* * Paste the name back together. */ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); p = name; while (namelen > 0) { int len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; memcpy(p, nc->l_array.la_array, len); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); } /* * Assume the first eight bytes of the value are * a uint64_t. */ value = fzap_leaf_value(&zl, zc); //printf("%s 0x%jx\n", name, (uintmax_t)value); - printf("%s\n", name); + callback((const char *)name); } } return (0); } +static int zfs_printf(const char *name) +{ + + printf("%s\n", name); + + return (0); +} + /* * List a zap directory. */ static int zap_list(const spa_t *spa, const dnode_phys_t *dnode) { uint64_t zap_type; size_t size = dnode->dn_datablkszsec * 512; if (dnode_read(spa, dnode, 0, zap_scratch, size)) return (EIO); zap_type = *(uint64_t *) zap_scratch; if (zap_type == ZBT_MICRO) - return mzap_list(dnode); + return mzap_list(dnode, zfs_printf); else - return fzap_list(spa, dnode); + return fzap_list(spa, dnode, zfs_printf); } static int objset_get_dnode(const spa_t *spa, const objset_phys_t *os, uint64_t objnum, dnode_phys_t *dnode) { off_t offset; offset = objnum * sizeof(dnode_phys_t); return dnode_read(spa, &os->os_meta_dnode, offset, dnode, sizeof(dnode_phys_t)); } static int mzap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) { const mzap_phys_t *mz; const mzap_ent_phys_t *mze; size_t size; int chunks, i; /* * Microzap objects use exactly one block. Read the whole * thing. */ size = dnode->dn_datablkszsec * 512; mz = (const mzap_phys_t *) zap_scratch; chunks = size / MZAP_ENT_LEN - 1; for (i = 0; i < chunks; i++) { mze = &mz->mz_chunk[i]; if (value == mze->mze_value) { strcpy(name, mze->mze_name); return (0); } } return (ENOENT); } static void fzap_name_copy(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, char *name) { size_t namelen; const zap_leaf_chunk_t *nc; char *p; namelen = zc->l_entry.le_name_numints; nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk); p = name; while (namelen > 0) { size_t len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; memcpy(p, nc->l_array.la_array, len); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next); } *p = '\0'; } static int fzap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) { int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; zap_phys_t zh = *(zap_phys_t *) zap_scratch; fat_zap_t z; int i, j; if (zh.zap_magic != ZAP_MAGIC) return (EIO); z.zap_block_shift = ilog2(bsize); z.zap_phys = (zap_phys_t *) zap_scratch; /* * This assumes that the leaf blocks start at block 1. The * documentation isn't exactly clear on this. */ zap_leaf_t zl; zl.l_bs = z.zap_block_shift; for (i = 0; i < zh.zap_num_leafs; i++) { off_t off = (i + 1) << zl.l_bs; if (dnode_read(spa, dnode, off, zap_scratch, bsize)) return (EIO); zl.l_phys = (zap_leaf_phys_t *) zap_scratch; for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) { zap_leaf_chunk_t *zc; zc = &ZAP_LEAF_CHUNK(&zl, j); if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) continue; if (zc->l_entry.le_value_intlen != 8 || zc->l_entry.le_value_numints != 1) continue; if (fzap_leaf_value(&zl, zc) == value) { fzap_name_copy(&zl, zc, name); return (0); } } } return (ENOENT); } static int zap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) { int rc; uint64_t zap_type; size_t size = dnode->dn_datablkszsec * 512; rc = dnode_read(spa, dnode, 0, zap_scratch, size); if (rc) return (rc); zap_type = *(uint64_t *) zap_scratch; if (zap_type == ZBT_MICRO) return mzap_rlookup(spa, dnode, name, value); else return fzap_rlookup(spa, dnode, name, value); } static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result) { char name[256]; char component[256]; uint64_t dir_obj, parent_obj, child_dir_zapobj; dnode_phys_t child_dir_zap, dataset, dir, parent; dsl_dir_phys_t *dd; dsl_dataset_phys_t *ds; char *p; int len; p = &name[sizeof(name) - 1]; *p = '\0'; if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum); return (EIO); } ds = (dsl_dataset_phys_t *)&dataset.dn_bonus; dir_obj = ds->ds_dir_obj; for (;;) { if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0) return (EIO); dd = (dsl_dir_phys_t *)&dir.dn_bonus; /* Actual loop condition. */ parent_obj = dd->dd_parent_obj; if (parent_obj == 0) break; if (objset_get_dnode(spa, &spa->spa_mos, parent_obj, &parent) != 0) return (EIO); dd = (dsl_dir_phys_t *)&parent.dn_bonus; child_dir_zapobj = dd->dd_child_dir_zapobj; if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) return (EIO); if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0) return (EIO); len = strlen(component); p -= len; memcpy(p, component, len); --p; *p = '/'; /* Actual loop iteration. */ dir_obj = parent_obj; } if (*p != '\0') ++p; strcpy(result, p); return (0); } static int zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum) { char element[256]; uint64_t dir_obj, child_dir_zapobj; dnode_phys_t child_dir_zap, dir; dsl_dir_phys_t *dd; const char *p, *q; if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) return (EIO); if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &dir_obj)) return (EIO); p = name; for (;;) { if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) return (EIO); dd = (dsl_dir_phys_t *)&dir.dn_bonus; while (*p == '/') p++; /* Actual loop condition #1. */ if (*p == '\0') break; q = strchr(p, '/'); if (q) { memcpy(element, p, q - p); element[q - p] = '\0'; p = q + 1; } else { strcpy(element, p); p += strlen(p); } child_dir_zapobj = dd->dd_child_dir_zapobj; if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) return (EIO); /* Actual loop condition #2. */ if (zap_lookup(spa, &child_dir_zap, element, &dir_obj) != 0) return (ENOENT); } *objnum = dd->dd_head_dataset_obj; return (0); } #ifndef BOOT2 static int zfs_list_dataset(const spa_t *spa, uint64_t objnum/*, int pos, char *entry*/) { uint64_t dir_obj, child_dir_zapobj; dnode_phys_t child_dir_zap, dir, dataset; dsl_dataset_phys_t *ds; dsl_dir_phys_t *dd; if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum); return (EIO); } ds = (dsl_dataset_phys_t *) &dataset.dn_bonus; dir_obj = ds->ds_dir_obj; if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) { printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj); return (EIO); } dd = (dsl_dir_phys_t *)&dir.dn_bonus; child_dir_zapobj = dd->dd_child_dir_zapobj; if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) { printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj); return (EIO); } return (zap_list(spa, &child_dir_zap) != 0); +} + +int +zfs_callback_dataset(const spa_t *spa, uint64_t objnum, int (*callback)(const char *name)) +{ + uint64_t dir_obj, child_dir_zapobj, zap_type; + dnode_phys_t child_dir_zap, dir, dataset; + dsl_dataset_phys_t *ds; + dsl_dir_phys_t *dd; + int err; + + err = objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset); + if (err != 0) { + printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum); + return (err); + } + ds = (dsl_dataset_phys_t *) &dataset.dn_bonus; + dir_obj = ds->ds_dir_obj; + + err = objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir); + if (err != 0) { + printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj); + return (err); + } + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + child_dir_zapobj = dd->dd_child_dir_zapobj; + err = objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap); + if (err != 0) { + printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj); + return (err); + } + + err = dnode_read(spa, &child_dir_zap, 0, zap_scratch, child_dir_zap.dn_datablkszsec * 512); + if (err != 0) + return (err); + + zap_type = *(uint64_t *) zap_scratch; + if (zap_type == ZBT_MICRO) + return mzap_list(&child_dir_zap, callback); + else + return fzap_list(spa, &child_dir_zap, callback); } #endif /* * Find the object set given the object number of its dataset object * and return its details in *objset */ static int zfs_mount_dataset(const spa_t *spa, uint64_t objnum, objset_phys_t *objset) { dnode_phys_t dataset; dsl_dataset_phys_t *ds; if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum); return (EIO); } ds = (dsl_dataset_phys_t *) &dataset.dn_bonus; if (zio_read(spa, &ds->ds_bp, objset)) { printf("ZFS: can't read object set for dataset %ju\n", (uintmax_t)objnum); return (EIO); } return (0); } /* * Find the object set pointed to by the BOOTFS property or the root * dataset if there is none and return its details in *objset */ static int zfs_get_root(const spa_t *spa, uint64_t *objid) { dnode_phys_t dir, propdir; uint64_t props, bootfs, root; *objid = 0; /* * Start with the MOS directory object. */ if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) { printf("ZFS: can't read MOS object directory\n"); return (EIO); } /* * Lookup the pool_props and see if we can find a bootfs. */ if (zap_lookup(spa, &dir, DMU_POOL_PROPS, &props) == 0 && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 && zap_lookup(spa, &propdir, "bootfs", &bootfs) == 0 && bootfs != 0) { *objid = bootfs; return (0); } /* * Lookup the root dataset directory */ if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &root) || objset_get_dnode(spa, &spa->spa_mos, root, &dir)) { printf("ZFS: can't find root dsl_dir\n"); return (EIO); } /* * Use the information from the dataset directory's bonus buffer * to find the dataset object and from that the object set itself. */ dsl_dir_phys_t *dd = (dsl_dir_phys_t *) &dir.dn_bonus; *objid = dd->dd_head_dataset_obj; return (0); } static int zfs_mount(const spa_t *spa, uint64_t rootobj, struct zfsmount *mount) { mount->spa = spa; /* * Find the root object set if not explicitly provided */ if (rootobj == 0 && zfs_get_root(spa, &rootobj)) { printf("ZFS: can't find root filesystem\n"); return (EIO); } if (zfs_mount_dataset(spa, rootobj, &mount->objset)) { printf("ZFS: can't open root filesystem\n"); return (EIO); } mount->rootobj = rootobj; return (0); } static int zfs_spa_init(spa_t *spa) { if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { printf("ZFS: can't read MOS of pool %s\n", spa->spa_name); return (EIO); } if (spa->spa_mos.os_type != DMU_OST_META) { printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name); return (EIO); } return (0); } static int zfs_dnode_stat(const spa_t *spa, dnode_phys_t *dn, struct stat *sb) { if (dn->dn_bonustype != DMU_OT_SA) { znode_phys_t *zp = (znode_phys_t *)dn->dn_bonus; sb->st_mode = zp->zp_mode; sb->st_uid = zp->zp_uid; sb->st_gid = zp->zp_gid; sb->st_size = zp->zp_size; } else { sa_hdr_phys_t *sahdrp; int hdrsize; size_t size = 0; void *buf = NULL; if (dn->dn_bonuslen != 0) sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn); else { if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) != 0) { blkptr_t *bp = &dn->dn_spill; int error; size = BP_GET_LSIZE(bp); buf = zfs_alloc(size); error = zio_read(spa, bp, buf); if (error != 0) { zfs_free(buf, size); return (error); } sahdrp = buf; } else { return (EIO); } } hdrsize = SA_HDR_SIZE(sahdrp); sb->st_mode = *(uint64_t *)((char *)sahdrp + hdrsize + SA_MODE_OFFSET); sb->st_uid = *(uint64_t *)((char *)sahdrp + hdrsize + SA_UID_OFFSET); sb->st_gid = *(uint64_t *)((char *)sahdrp + hdrsize + SA_GID_OFFSET); sb->st_size = *(uint64_t *)((char *)sahdrp + hdrsize + SA_SIZE_OFFSET); if (buf != NULL) zfs_free(buf, size); } return (0); } /* * Lookup a file and return its dnode. */ static int zfs_lookup(const struct zfsmount *mount, const char *upath, dnode_phys_t *dnode) { int rc; uint64_t objnum, rootnum, parentnum; const spa_t *spa; dnode_phys_t dn; const char *p, *q; char element[256]; char path[1024]; int symlinks_followed = 0; struct stat sb; spa = mount->spa; if (mount->objset.os_type != DMU_OST_ZFS) { printf("ZFS: unexpected object set type %ju\n", (uintmax_t)mount->objset.os_type); return (EIO); } /* * Get the root directory dnode. */ rc = objset_get_dnode(spa, &mount->objset, MASTER_NODE_OBJ, &dn); if (rc) return (rc); rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, &rootnum); if (rc) return (rc); rc = objset_get_dnode(spa, &mount->objset, rootnum, &dn); if (rc) return (rc); objnum = rootnum; p = upath; while (p && *p) { while (*p == '/') p++; if (!*p) break; q = strchr(p, '/'); if (q) { memcpy(element, p, q - p); element[q - p] = 0; p = q; } else { strcpy(element, p); p = 0; } rc = zfs_dnode_stat(spa, &dn, &sb); if (rc) return (rc); if (!S_ISDIR(sb.st_mode)) return (ENOTDIR); parentnum = objnum; rc = zap_lookup(spa, &dn, element, &objnum); if (rc) return (rc); objnum = ZFS_DIRENT_OBJ(objnum); rc = objset_get_dnode(spa, &mount->objset, objnum, &dn); if (rc) return (rc); /* * Check for symlink. */ rc = zfs_dnode_stat(spa, &dn, &sb); if (rc) return (rc); if (S_ISLNK(sb.st_mode)) { if (symlinks_followed > 10) return (EMLINK); symlinks_followed++; /* * Read the link value and copy the tail of our * current path onto the end. */ if (p) strcpy(&path[sb.st_size], p); else path[sb.st_size] = 0; if (sb.st_size + sizeof(znode_phys_t) <= dn.dn_bonuslen) { memcpy(path, &dn.dn_bonus[sizeof(znode_phys_t)], sb.st_size); } else { rc = dnode_read(spa, &dn, 0, path, sb.st_size); if (rc) return (rc); } /* * Restart with the new path, starting either at * the root or at the parent depending whether or * not the link is relative. */ p = path; if (*p == '/') objnum = rootnum; else objnum = parentnum; objset_get_dnode(spa, &mount->objset, objnum, &dn); } } *dnode = dn; return (0); } Index: projects/clang380-import/sys/boot =================================================================== --- projects/clang380-import/sys/boot (revision 293015) +++ projects/clang380-import/sys/boot (revision 293016) Property changes on: projects/clang380-import/sys/boot ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/boot:r292913-293015 Index: projects/clang380-import/sys/compat/linuxkpi/common/include/linux/cdev.h =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/include/linux/cdev.h (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/include/linux/cdev.h (revision 293016) @@ -1,132 +1,106 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 ``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. * * $FreeBSD$ */ #ifndef _LINUX_CDEV_H_ #define _LINUX_CDEV_H_ #include #include #include #include struct file_operations; struct inode; struct module; extern struct cdevsw linuxcdevsw; +extern const struct kobj_type linux_cdev_ktype; +extern const struct kobj_type linux_cdev_static_ktype; struct linux_cdev { struct kobject kobj; struct module *owner; struct cdev *cdev; dev_t dev; const struct file_operations *ops; }; static inline void -cdev_release(struct kobject *kobj) -{ - struct linux_cdev *cdev; - - cdev = container_of(kobj, struct linux_cdev, kobj); - if (cdev->cdev) - destroy_dev(cdev->cdev); - kfree(cdev); -} - -static inline void -cdev_static_release(struct kobject *kobj) -{ - struct linux_cdev *cdev; - - cdev = container_of(kobj, struct linux_cdev, kobj); - if (cdev->cdev) - destroy_dev(cdev->cdev); -} - -static struct kobj_type cdev_ktype = { - .release = cdev_release, -}; - -static struct kobj_type cdev_static_ktype = { - .release = cdev_static_release, -}; - -static inline void cdev_init(struct linux_cdev *cdev, const struct file_operations *ops) { - kobject_init(&cdev->kobj, &cdev_static_ktype); + kobject_init(&cdev->kobj, &linux_cdev_static_ktype); cdev->ops = ops; } static inline struct linux_cdev * cdev_alloc(void) { struct linux_cdev *cdev; cdev = kzalloc(sizeof(struct linux_cdev), M_WAITOK); if (cdev) - kobject_init(&cdev->kobj, &cdev_ktype); + kobject_init(&cdev->kobj, &linux_cdev_ktype); return (cdev); } static inline void cdev_put(struct linux_cdev *p) { kobject_put(&p->kobj); } static inline int cdev_add(struct linux_cdev *cdev, dev_t dev, unsigned count) { if (count != 1) panic("cdev_add: Unsupported count: %d", count); cdev->cdev = make_dev(&linuxcdevsw, MINOR(dev), 0, 0, 0700, "%s", kobject_name(&cdev->kobj)); cdev->dev = dev; cdev->cdev->si_drv1 = cdev; + kobject_get(cdev->kobj.parent); return (0); } static inline void cdev_del(struct linux_cdev *cdev) { if (cdev->cdev) { destroy_dev(cdev->cdev); cdev->cdev = NULL; } kobject_put(&cdev->kobj); } #define cdev linux_cdev #endif /* _LINUX_CDEV_H_ */ Index: projects/clang380-import/sys/compat/linuxkpi/common/include/linux/device.h =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/include/linux/device.h (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/include/linux/device.h (revision 293016) @@ -1,425 +1,332 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 ``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. * * $FreeBSD$ */ #ifndef _LINUX_DEVICE_H_ #define _LINUX_DEVICE_H_ #include #include #include #include #include #include #include #include #include #include #include #include enum irqreturn { IRQ_NONE = 0, IRQ_HANDLED, IRQ_WAKE_THREAD, }; typedef enum irqreturn irqreturn_t; struct class { const char *name; struct module *owner; struct kobject kobj; devclass_t bsdclass; void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); char * (*devnode)(struct device *dev, umode_t *mode); }; struct device { struct device *parent; struct list_head irqents; device_t bsddev; dev_t devt; struct class *class; void (*release)(struct device *dev); struct kobject kobj; uint64_t *dma_mask; void *driver_data; unsigned int irq; unsigned int msix; unsigned int msix_max; }; -extern struct device linux_rootdev; -extern struct kobject class_root; +extern struct device linux_root_device; +extern struct kobject linux_class_root; +extern const struct kobj_type linux_dev_ktype; +extern const struct kobj_type linux_class_ktype; struct class_attribute { struct attribute attr; ssize_t (*show)(struct class *, struct class_attribute *, char *); ssize_t (*store)(struct class *, struct class_attribute *, const char *, size_t); const void *(*namespace)(struct class *, const struct class_attribute *); }; #define CLASS_ATTR(_name, _mode, _show, _store) \ struct class_attribute class_attr_##_name = \ { { #_name, NULL, _mode }, _show, _store } struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *, struct device_attribute *, char *); ssize_t (*store)(struct device *, struct device_attribute *, const char *, size_t); }; #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = \ { { #_name, NULL, _mode }, _show, _store } /* Simple class attribute that is just a static string */ struct class_attribute_string { struct class_attribute attr; char *str; }; static inline ssize_t show_class_attr_string(struct class *class, struct class_attribute *attr, char *buf) { struct class_attribute_string *cs; cs = container_of(attr, struct class_attribute_string, attr); return snprintf(buf, PAGE_SIZE, "%s\n", cs->str); } /* Currently read-only only */ #define _CLASS_ATTR_STRING(_name, _mode, _str) \ { __ATTR(_name, _mode, show_class_attr_string, NULL), _str } #define CLASS_ATTR_STRING(_name, _mode, _str) \ struct class_attribute_string class_attr_##_name = \ _CLASS_ATTR_STRING(_name, _mode, _str) #define dev_err(dev, fmt, ...) device_printf((dev)->bsddev, fmt, ##__VA_ARGS__) #define dev_warn(dev, fmt, ...) device_printf((dev)->bsddev, fmt, ##__VA_ARGS__) #define dev_info(dev, fmt, ...) device_printf((dev)->bsddev, fmt, ##__VA_ARGS__) #define dev_printk(lvl, dev, fmt, ...) \ device_printf((dev)->bsddev, fmt, ##__VA_ARGS__) static inline void * dev_get_drvdata(struct device *dev) { return dev->driver_data; } static inline void dev_set_drvdata(struct device *dev, void *data) { dev->driver_data = data; } static inline struct device * get_device(struct device *dev) { if (dev) kobject_get(&dev->kobj); return (dev); } static inline char * dev_name(const struct device *dev) { return kobject_name(&dev->kobj); } #define dev_set_name(_dev, _fmt, ...) \ kobject_set_name(&(_dev)->kobj, (_fmt), ##__VA_ARGS__) static inline void put_device(struct device *dev) { if (dev) kobject_put(&dev->kobj); } -static inline ssize_t -class_show(struct kobject *kobj, struct attribute *attr, char *buf) -{ - struct class_attribute *dattr; - ssize_t error; - - dattr = container_of(attr, struct class_attribute, attr); - error = -EIO; - if (dattr->show) - error = dattr->show(container_of(kobj, struct class, kobj), - dattr, buf); - return (error); -} - -static inline ssize_t -class_store(struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) -{ - struct class_attribute *dattr; - ssize_t error; - - dattr = container_of(attr, struct class_attribute, attr); - error = -EIO; - if (dattr->store) - error = dattr->store(container_of(kobj, struct class, kobj), - dattr, buf, count); - return (error); -} - -static inline void -class_release(struct kobject *kobj) -{ - struct class *class; - - class = container_of(kobj, struct class, kobj); - if (class->class_release) - class->class_release(class); -} - -static struct sysfs_ops class_sysfs = { - .show = class_show, - .store = class_store, -}; -static struct kobj_type class_ktype = { - .release = class_release, - .sysfs_ops = &class_sysfs -}; - static inline int class_register(struct class *class) { class->bsdclass = devclass_create(class->name); - kobject_init(&class->kobj, &class_ktype); + kobject_init(&class->kobj, &linux_class_ktype); kobject_set_name(&class->kobj, class->name); - kobject_add(&class->kobj, &class_root, class->name); + kobject_add(&class->kobj, &linux_class_root, class->name); return (0); } static inline void class_unregister(struct class *class) { kobject_put(&class->kobj); } -static inline void -device_release(struct kobject *kobj) -{ - struct device *dev; - - dev = container_of(kobj, struct device, kobj); - /* This is the precedence defined by linux. */ - if (dev->release) - dev->release(dev); - else if (dev->class && dev->class->dev_release) - dev->class->dev_release(dev); -} - -static inline ssize_t -dev_show(struct kobject *kobj, struct attribute *attr, char *buf) -{ - struct device_attribute *dattr; - ssize_t error; - - dattr = container_of(attr, struct device_attribute, attr); - error = -EIO; - if (dattr->show) - error = dattr->show(container_of(kobj, struct device, kobj), - dattr, buf); - return (error); -} - -static inline ssize_t -dev_store(struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) -{ - struct device_attribute *dattr; - ssize_t error; - - dattr = container_of(attr, struct device_attribute, attr); - error = -EIO; - if (dattr->store) - error = dattr->store(container_of(kobj, struct device, kobj), - dattr, buf, count); - return (error); -} - -static struct sysfs_ops dev_sysfs = { .show = dev_show, .store = dev_store, }; -static struct kobj_type dev_ktype = { - .release = device_release, - .sysfs_ops = &dev_sysfs -}; - /* * Devices are registered and created for exporting to sysfs. create * implies register and register assumes the device fields have been * setup appropriately before being called. */ static inline int device_register(struct device *dev) { device_t bsddev; int unit; bsddev = NULL; if (dev->devt) { unit = MINOR(dev->devt); bsddev = devclass_get_device(dev->class->bsdclass, unit); } else unit = -1; if (bsddev == NULL) bsddev = device_add_child(dev->parent->bsddev, dev->class->kobj.name, unit); if (bsddev) { if (dev->devt == 0) dev->devt = makedev(0, device_get_unit(bsddev)); device_set_softc(bsddev, dev); } dev->bsddev = bsddev; - kobject_init(&dev->kobj, &dev_ktype); + kobject_init(&dev->kobj, &linux_dev_ktype); kobject_add(&dev->kobj, &dev->class->kobj, dev_name(dev)); return (0); } static inline void device_unregister(struct device *dev) { device_t bsddev; bsddev = dev->bsddev; mtx_lock(&Giant); if (bsddev) device_delete_child(device_get_parent(bsddev), bsddev); mtx_unlock(&Giant); put_device(dev); } struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...); static inline void device_destroy(struct class *class, dev_t devt) { device_t bsddev; int unit; unit = MINOR(devt); bsddev = devclass_get_device(class->bsdclass, unit); if (bsddev) device_unregister(device_get_softc(bsddev)); } static inline void -class_kfree(struct class *class) +linux_class_kfree(struct class *class) { kfree(class); } static inline struct class * class_create(struct module *owner, const char *name) { struct class *class; int error; class = kzalloc(sizeof(*class), M_WAITOK); class->owner = owner; class->name= name; - class->class_release = class_kfree; + class->class_release = linux_class_kfree; error = class_register(class); if (error) { kfree(class); return (NULL); } return (class); } static inline void class_destroy(struct class *class) { if (class == NULL) return; class_unregister(class); } static inline int device_create_file(struct device *dev, const struct device_attribute *attr) { if (dev) return sysfs_create_file(&dev->kobj, &attr->attr); return -EINVAL; } static inline void device_remove_file(struct device *dev, const struct device_attribute *attr) { if (dev) sysfs_remove_file(&dev->kobj, &attr->attr); } static inline int class_create_file(struct class *class, const struct class_attribute *attr) { if (class) return sysfs_create_file(&class->kobj, &attr->attr); return -EINVAL; } static inline void class_remove_file(struct class *class, const struct class_attribute *attr) { if (class) sysfs_remove_file(&class->kobj, &attr->attr); } -static inline int dev_to_node(struct device *dev) +static inline int +dev_to_node(struct device *dev) { return -1; } char *kvasprintf(gfp_t, const char *, va_list); char *kasprintf(gfp_t, const char *, ...); #endif /* _LINUX_DEVICE_H_ */ Index: projects/clang380-import/sys/compat/linuxkpi/common/include/linux/file.h =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/include/linux/file.h (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/include/linux/file.h (revision 293016) @@ -1,175 +1,176 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. * 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 ``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. * * $FreeBSD$ */ #ifndef _LINUX_FILE_H_ #define _LINUX_FILE_H_ #include #include #include #include #include #include #include struct linux_file; #undef file extern struct fileops linuxfileops; static inline struct linux_file * linux_fget(unsigned int fd) { cap_rights_t rights; struct file *file; if (fget_unlocked(curthread->td_proc->p_fd, fd, cap_rights_init(&rights), &file, NULL) != 0) { return (NULL); } return (struct linux_file *)file->f_data; } static inline void fput(struct linux_file *filp) { if (filp->_file == NULL) { kfree(filp); return; } if (refcount_release(&filp->_file->f_count)) { _fdrop(filp->_file, curthread); kfree(filp); } } static inline void put_unused_fd(unsigned int fd) { cap_rights_t rights; struct file *file; if (fget_unlocked(curthread->td_proc->p_fd, fd, cap_rights_init(&rights), &file, NULL) != 0) { return; } /* * NOTE: We should only get here when the "fd" has not been * installed, so no need to free the associated Linux file * structure. */ fdclose(curthread, file, fd); /* drop extra reference */ fdrop(file, curthread); } static inline void fd_install(unsigned int fd, struct linux_file *filp) { cap_rights_t rights; struct file *file; if (fget_unlocked(curthread->td_proc->p_fd, fd, cap_rights_init(&rights), &file, NULL) != 0) { - file = NULL; + filp->_file = NULL; + } else { + filp->_file = file; + finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops); } - filp->_file = file; - finit(file, filp->f_mode, DTYPE_DEV, filp, &linuxfileops); /* drop the extra reference */ fput(filp); } static inline int get_unused_fd(void) { struct file *file; int error; int fd; error = falloc(curthread, &file, &fd, 0); if (error) return -error; /* drop the extra reference */ fdrop(file, curthread); return fd; } static inline int get_unused_fd_flags(int flags) { struct file *file; int error; int fd; error = falloc(curthread, &file, &fd, flags); if (error) return -error; /* drop the extra reference */ fdrop(file, curthread); return fd; } static inline struct linux_file * alloc_file(int mode, const struct file_operations *fops) { struct linux_file *filp; filp = kzalloc(sizeof(*filp), GFP_KERNEL); if (filp == NULL) return (NULL); filp->f_op = fops; filp->f_mode = mode; return filp; } struct fd { struct linux_file *linux_file; }; static inline void fdput(struct fd fd) { fput(fd.linux_file); } static inline struct fd fdget(unsigned int fd) { struct linux_file *f = linux_fget(fd); return (struct fd){f}; } #define file linux_file #define fget linux_fget #endif /* _LINUX_FILE_H_ */ Index: projects/clang380-import/sys/compat/linuxkpi/common/include/linux/kobject.h =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/include/linux/kobject.h (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/include/linux/kobject.h (revision 293016) @@ -1,150 +1,148 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 ``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. * * $FreeBSD$ */ #ifndef _LINUX_KOBJECT_H_ #define _LINUX_KOBJECT_H_ #include #include #include #include struct kobject; struct sysctl_oid; struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; }; -extern struct kobj_type kfree_type; +extern const struct kobj_type linux_kfree_type; struct kobject { struct kobject *parent; char *name; struct kref kref; - struct kobj_type *ktype; + const struct kobj_type *ktype; struct list_head entry; struct sysctl_oid *oidp; }; extern struct kobject *mm_kobj; struct attribute { const char *name; struct module *owner; mode_t mode; }; struct kobj_attribute { struct attribute attr; ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); }; static inline void -kobject_init(struct kobject *kobj, struct kobj_type *ktype) +kobject_init(struct kobject *kobj, const struct kobj_type *ktype) { kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); kobj->ktype = ktype; kobj->oidp = NULL; } -static inline void kobject_put(struct kobject *kobj); -void kobject_release(struct kref *kref); +void linux_kobject_release(struct kref *kref); static inline void kobject_put(struct kobject *kobj) { if (kobj) - kref_put(&kobj->kref, kobject_release); + kref_put(&kobj->kref, linux_kobject_release); } static inline struct kobject * kobject_get(struct kobject *kobj) { if (kobj) kref_get(&kobj->kref); return kobj; } int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list); int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...); static inline struct kobject * kobject_create(void) { struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (kobj == NULL) return (NULL); - kobject_init(kobj, &kfree_type); + kobject_init(kobj, &linux_kfree_type); return (kobj); } static inline struct kobject * kobject_create_and_add(const char *name, struct kobject *parent) { struct kobject *kobj; kobj = kobject_create(); if (kobj == NULL) return (NULL); if (kobject_add(kobj, parent, "%s", name) == 0) return (kobj); kobject_put(kobj); return (NULL); } - static inline char * kobject_name(const struct kobject *kobj) { return kobj->name; } int kobject_set_name(struct kobject *kobj, const char *fmt, ...); -int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, +int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); #endif /* _LINUX_KOBJECT_H_ */ Index: projects/clang380-import/sys/compat/linuxkpi/common/include/linux/miscdevice.h =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/include/linux/miscdevice.h (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/include/linux/miscdevice.h (revision 293016) @@ -1,76 +1,76 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 ``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. * * $FreeBSD$ */ #ifndef _LINUX_MISCDEVICE_H_ #define _LINUX_MISCDEVICE_H_ #define MISC_DYNAMIC_MINOR -1 #include #include struct miscdevice { const char *name; struct device *this_device; const struct file_operations *fops; struct cdev *cdev; int minor; const char *nodename; umode_t mode; }; -extern struct class miscclass; +extern struct class linux_class_misc; static inline int misc_register(struct miscdevice *misc) { - misc->this_device = device_create(&miscclass, &linux_rootdev, 0, misc, - misc->name); + misc->this_device = device_create(&linux_class_misc, + &linux_root_device, 0, misc, misc->name); misc->cdev = cdev_alloc(); if (misc->cdev == NULL) return -ENOMEM; misc->cdev->owner = THIS_MODULE; misc->cdev->ops = misc->fops; kobject_set_name(&misc->cdev->kobj, misc->name); if (cdev_add(misc->cdev, misc->this_device->devt, 1)) return -EINVAL; return (0); } static inline int misc_deregister(struct miscdevice *misc) { - device_destroy(&miscclass, misc->this_device->devt); + device_destroy(&linux_class_misc, misc->this_device->devt); cdev_del(misc->cdev); return (0); } #endif /* _LINUX_MISCDEVICE_H_ */ Index: projects/clang380-import/sys/compat/linuxkpi/common/src/linux_compat.c =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/src/linux_compat.c (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/src/linux_compat.c (revision 293016) @@ -1,1048 +1,1183 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013-2015 Mellanox Technologies, Ltd. * 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 ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #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 #include #include #include #include #include #include MALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat"); #include /* Undo Linux compat changes. */ #undef RB_ROOT #undef file #undef cdev #define RB_ROOT(head) (head)->rbh_root -struct kobject class_root; -struct device linux_rootdev; -struct class miscclass; +struct kobject linux_class_root; +struct device linux_root_device; +struct class linux_class_misc; struct list_head pci_drivers; struct list_head pci_devices; struct net init_net; spinlock_t pci_lock; unsigned long linux_timer_hz_mask; int panic_cmp(struct rb_node *one, struct rb_node *two) { panic("no cmp"); } RB_GENERATE(linux_root, rb_node, __entry, panic_cmp); int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list args) { va_list tmp_va; int len; char *old; char *name; char dummy; old = kobj->name; if (old && fmt == NULL) return (0); /* compute length of string */ va_copy(tmp_va, args); len = vsnprintf(&dummy, 0, fmt, tmp_va); va_end(tmp_va); /* account for zero termination */ len++; /* check for error */ if (len < 1) return (-EINVAL); /* allocate memory for string */ name = kzalloc(len, GFP_KERNEL); if (name == NULL) return (-ENOMEM); vsnprintf(name, len, fmt, args); kobj->name = name; /* free old string */ kfree(old); /* filter new string */ for (; *name != '\0'; name++) if (*name == '/') *name = '!'; return (0); } int kobject_set_name(struct kobject *kobj, const char *fmt, ...) { va_list args; int error; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); return (error); } -static inline int +static int kobject_add_complete(struct kobject *kobj, struct kobject *parent) { - struct kobj_type *t; + const struct kobj_type *t; int error; - kobj->parent = kobject_get(parent); + kobj->parent = parent; error = sysfs_create_dir(kobj); if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) { struct attribute **attr; t = kobj->ktype; for (attr = t->default_attrs; *attr != NULL; attr++) { error = sysfs_create_file(kobj, *attr); if (error) break; } if (error) sysfs_remove_dir(kobj); } return (error); } int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) { va_list args; int error; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); if (error) return (error); return kobject_add_complete(kobj, parent); } void -kobject_release(struct kref *kref) +linux_kobject_release(struct kref *kref) { struct kobject *kobj; char *name; kobj = container_of(kref, struct kobject, kref); sysfs_remove_dir(kobj); - if (kobj->parent) - kobject_put(kobj->parent); - kobj->parent = NULL; name = kobj->name; if (kobj->ktype && kobj->ktype->release) kobj->ktype->release(kobj); kfree(name); } static void -kobject_kfree(struct kobject *kobj) +linux_kobject_kfree(struct kobject *kobj) { kfree(kobj); } static void -kobject_kfree_name(struct kobject *kobj) +linux_kobject_kfree_name(struct kobject *kobj) { if (kobj) { kfree(kobj->name); } } -struct kobj_type kfree_type = { .release = kobject_kfree }; +const struct kobj_type linux_kfree_type = { + .release = linux_kobject_kfree +}; static void -dev_release(struct device *dev) +linux_device_release(struct device *dev) { - pr_debug("dev_release: %s\n", dev_name(dev)); + pr_debug("linux_device_release: %s\n", dev_name(dev)); kfree(dev); } +static ssize_t +linux_class_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct class_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct class_attribute, attr); + error = -EIO; + if (dattr->show) + error = dattr->show(container_of(kobj, struct class, kobj), + dattr, buf); + return (error); +} + +static ssize_t +linux_class_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct class_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct class_attribute, attr); + error = -EIO; + if (dattr->store) + error = dattr->store(container_of(kobj, struct class, kobj), + dattr, buf, count); + return (error); +} + +static void +linux_class_release(struct kobject *kobj) +{ + struct class *class; + + class = container_of(kobj, struct class, kobj); + if (class->class_release) + class->class_release(class); +} + +static const struct sysfs_ops linux_class_sysfs = { + .show = linux_class_show, + .store = linux_class_store, +}; + +const struct kobj_type linux_class_ktype = { + .release = linux_class_release, + .sysfs_ops = &linux_class_sysfs +}; + +static void +linux_dev_release(struct kobject *kobj) +{ + struct device *dev; + + dev = container_of(kobj, struct device, kobj); + /* This is the precedence defined by linux. */ + if (dev->release) + dev->release(dev); + else if (dev->class && dev->class->dev_release) + dev->class->dev_release(dev); +} + +static ssize_t +linux_dev_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct device_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct device_attribute, attr); + error = -EIO; + if (dattr->show) + error = dattr->show(container_of(kobj, struct device, kobj), + dattr, buf); + return (error); +} + +static ssize_t +linux_dev_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct device_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct device_attribute, attr); + error = -EIO; + if (dattr->store) + error = dattr->store(container_of(kobj, struct device, kobj), + dattr, buf, count); + return (error); +} + +static const struct sysfs_ops linux_dev_sysfs = { + .show = linux_dev_show, + .store = linux_dev_store, +}; + +const struct kobj_type linux_dev_ktype = { + .release = linux_dev_release, + .sysfs_ops = &linux_dev_sysfs +}; + struct device * device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) { struct device *dev; va_list args; dev = kzalloc(sizeof(*dev), M_WAITOK); dev->parent = parent; dev->class = class; dev->devt = devt; dev->driver_data = drvdata; - dev->release = dev_release; + dev->release = linux_device_release; va_start(args, fmt); kobject_set_name_vargs(&dev->kobj, fmt, args); va_end(args); device_register(dev); return (dev); } int -kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, +kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; int error; kobject_init(kobj, ktype); kobj->ktype = ktype; kobj->parent = parent; kobj->name = NULL; va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); if (error) return (error); return kobject_add_complete(kobj, parent); } static void linux_file_dtor(void *cdp) { struct linux_file *filp; filp = cdp; filp->f_op->release(filp->f_vnode, filp); vdrop(filp->f_vnode); kfree(filp); } static int linux_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (ENODEV); filp = kzalloc(sizeof(*filp), GFP_KERNEL); filp->f_dentry = &filp->f_dentry_store; filp->f_op = ldev->ops; filp->f_flags = file->f_flag; vhold(file->f_vnode); filp->f_vnode = file->f_vnode; if (filp->f_op->open) { error = -filp->f_op->open(file->f_vnode, filp); if (error) { kfree(filp); return (error); } } error = devfs_set_cdevpriv(filp, linux_file_dtor); if (error) { filp->f_op->release(file->f_vnode, filp); kfree(filp); return (error); } return 0; } static int linux_dev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; devfs_clear_cdevpriv(); return (0); } static int linux_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; /* * Linux does not have a generic ioctl copyin/copyout layer. All * linux ioctls must be converted to void ioctls which pass a * pointer to the address of the data. We want the actual user * address so we dereference here. */ data = *(void **)data; if (filp->f_op->unlocked_ioctl) error = -filp->f_op->unlocked_ioctl(filp, cmd, (u_long)data); else error = ENOTTY; return (error); } static int linux_dev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; ssize_t bytes; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_dev_read: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_dev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; ssize_t bytes; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_dev_write: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->write) { bytes = filp->f_op->write(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_dev_poll(struct cdev *dev, int events, struct thread *td) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; int revents; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (0); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; return (revents); } static int linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot) { struct linux_cdev *ldev; struct linux_file *filp; struct file *file; struct vm_area_struct vma; int error; file = curthread->td_fpop; ldev = dev->si_drv1; if (ldev == NULL) return (ENODEV); if ((error = devfs_get_cdevpriv((void **)&filp)) != 0) return (error); filp->f_flags = file->f_flag; vma.vm_start = 0; vma.vm_end = size; vma.vm_pgoff = *offset / PAGE_SIZE; vma.vm_pfn = 0; vma.vm_page_prot = 0; if (filp->f_op->mmap) { error = -filp->f_op->mmap(filp, &vma); if (error == 0) { struct sglist *sg; sg = sglist_alloc(1, M_WAITOK); sglist_append_phys(sg, (vm_paddr_t)vma.vm_pfn << PAGE_SHIFT, vma.vm_len); *object = vm_pager_allocate(OBJT_SG, sg, vma.vm_len, nprot, 0, curthread->td_ucred); if (*object == NULL) { sglist_free(sg); return (EINVAL); } *offset = 0; if (vma.vm_page_prot != VM_MEMATTR_DEFAULT) { VM_OBJECT_WLOCK(*object); vm_object_set_memattr(*object, vma.vm_page_prot); VM_OBJECT_WUNLOCK(*object); } } } else error = ENODEV; return (error); } struct cdevsw linuxcdevsw = { .d_version = D_VERSION, .d_flags = D_TRACKCLOSE, .d_open = linux_dev_open, .d_close = linux_dev_close, .d_read = linux_dev_read, .d_write = linux_dev_write, .d_ioctl = linux_dev_ioctl, .d_mmap_single = linux_dev_mmap_single, .d_poll = linux_dev_poll, }; static int linux_file_read(struct file *file, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct linux_file *filp; ssize_t bytes; int error; error = 0; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; if (uio->uio_iovcnt != 1) panic("linux_file_read: uio %p iovcnt %d", uio, uio->uio_iovcnt); if (filp->f_op->read) { bytes = filp->f_op->read(filp, uio->uio_iov->iov_base, uio->uio_iov->iov_len, &uio->uio_offset); if (bytes >= 0) { uio->uio_iov->iov_base = ((uint8_t *)uio->uio_iov->iov_base) + bytes; uio->uio_iov->iov_len -= bytes; uio->uio_resid -= bytes; } else error = -bytes; } else error = ENXIO; return (error); } static int linux_file_poll(struct file *file, int events, struct ucred *active_cred, struct thread *td) { struct linux_file *filp; int revents; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; if (filp->f_op->poll) revents = filp->f_op->poll(filp, NULL) & events; else revents = 0; return (0); } static int linux_file_close(struct file *file, struct thread *td) { struct linux_file *filp; int error; filp = (struct linux_file *)file->f_data; filp->f_flags = file->f_flag; error = -filp->f_op->release(NULL, filp); funsetown(&filp->f_sigio); kfree(filp); return (error); } static int linux_file_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *cred, struct thread *td) { struct linux_file *filp; int error; filp = (struct linux_file *)fp->f_data; filp->f_flags = fp->f_flag; error = 0; switch (cmd) { case FIONBIO: break; case FIOASYNC: if (filp->f_op->fasync == NULL) break; error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); break; case FIOSETOWN: error = fsetown(*(int *)data, &filp->f_sigio); if (error == 0) error = filp->f_op->fasync(0, filp, fp->f_flag & FASYNC); break; case FIOGETOWN: *(int *)data = fgetown(&filp->f_sigio); break; default: error = ENOTTY; break; } return (error); } static int linux_file_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td) { return (EOPNOTSUPP); } static int linux_file_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { return (0); } struct fileops linuxfileops = { .fo_read = linux_file_read, .fo_write = invfo_rdwr, .fo_truncate = invfo_truncate, .fo_kqfilter = invfo_kqfilter, .fo_stat = linux_file_stat, .fo_fill_kinfo = linux_file_fill_kinfo, .fo_poll = linux_file_poll, .fo_close = linux_file_close, .fo_ioctl = linux_file_ioctl, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, }; /* * Hash of vmmap addresses. This is infrequently accessed and does not * need to be particularly large. This is done because we must store the * caller's idea of the map size to properly unmap. */ struct vmmap { LIST_ENTRY(vmmap) vm_next; void *vm_addr; unsigned long vm_size; }; struct vmmaphd { struct vmmap *lh_first; }; #define VMMAP_HASH_SIZE 64 #define VMMAP_HASH_MASK (VMMAP_HASH_SIZE - 1) #define VM_HASH(addr) ((uintptr_t)(addr) >> PAGE_SHIFT) & VMMAP_HASH_MASK static struct vmmaphd vmmaphead[VMMAP_HASH_SIZE]; static struct mtx vmmaplock; static void vmmap_add(void *addr, unsigned long size) { struct vmmap *vmmap; vmmap = kmalloc(sizeof(*vmmap), GFP_KERNEL); mtx_lock(&vmmaplock); vmmap->vm_size = size; vmmap->vm_addr = addr; LIST_INSERT_HEAD(&vmmaphead[VM_HASH(addr)], vmmap, vm_next); mtx_unlock(&vmmaplock); } static struct vmmap * vmmap_remove(void *addr) { struct vmmap *vmmap; mtx_lock(&vmmaplock); LIST_FOREACH(vmmap, &vmmaphead[VM_HASH(addr)], vm_next) if (vmmap->vm_addr == addr) break; if (vmmap) LIST_REMOVE(vmmap, vm_next); mtx_unlock(&vmmaplock); return (vmmap); } #if defined(__i386__) || defined(__amd64__) void * _ioremap_attr(vm_paddr_t phys_addr, unsigned long size, int attr) { void *addr; addr = pmap_mapdev_attr(phys_addr, size, attr); if (addr == NULL) return (NULL); vmmap_add(addr, size); return (addr); } #endif void iounmap(void *addr) { struct vmmap *vmmap; vmmap = vmmap_remove(addr); if (vmmap == NULL) return; #if defined(__i386__) || defined(__amd64__) pmap_unmapdev((vm_offset_t)addr, vmmap->vm_size); #endif kfree(vmmap); } void * vmap(struct page **pages, unsigned int count, unsigned long flags, int prot) { vm_offset_t off; size_t size; size = count * PAGE_SIZE; off = kva_alloc(size); if (off == 0) return (NULL); vmmap_add((void *)off, size); pmap_qenter(off, pages, count); return ((void *)off); } void vunmap(void *addr) { struct vmmap *vmmap; vmmap = vmmap_remove(addr); if (vmmap == NULL) return; pmap_qremove((vm_offset_t)addr, vmmap->vm_size / PAGE_SIZE); kva_free((vm_offset_t)addr, vmmap->vm_size); kfree(vmmap); } char * kvasprintf(gfp_t gfp, const char *fmt, va_list ap) { unsigned int len; char *p; va_list aq; va_copy(aq, ap); len = vsnprintf(NULL, 0, fmt, aq); va_end(aq); p = kmalloc(len + 1, gfp); if (p != NULL) vsnprintf(p, len + 1, fmt, ap); return (p); } char * kasprintf(gfp_t gfp, const char *fmt, ...) { va_list ap; char *p; va_start(ap, fmt); p = kvasprintf(gfp, fmt, ap); va_end(ap); return (p); } static int linux_timer_jiffies_until(unsigned long expires) { int delta = expires - jiffies; /* guard against already expired values */ if (delta < 1) delta = 1; return (delta); } static void linux_timer_callback_wrapper(void *context) { struct timer_list *timer; timer = context; timer->function(timer->data); } void mod_timer(struct timer_list *timer, unsigned long expires) { timer->expires = expires; callout_reset(&timer->timer_callout, linux_timer_jiffies_until(expires), &linux_timer_callback_wrapper, timer); } void add_timer(struct timer_list *timer) { callout_reset(&timer->timer_callout, linux_timer_jiffies_until(timer->expires), &linux_timer_callback_wrapper, timer); } static void linux_timer_init(void *arg) { /* * Compute an internal HZ value which can divide 2**32 to * avoid timer rounding problems when the tick value wraps * around 2**32: */ linux_timer_hz_mask = 1; while (linux_timer_hz_mask < (unsigned long)hz) linux_timer_hz_mask *= 2; linux_timer_hz_mask--; } SYSINIT(linux_timer, SI_SUB_DRIVERS, SI_ORDER_FIRST, linux_timer_init, NULL); void linux_complete_common(struct completion *c, int all) { int wakeup_swapper; sleepq_lock(c); c->done++; if (all) wakeup_swapper = sleepq_broadcast(c, SLEEPQ_SLEEP, 0, 0); else wakeup_swapper = sleepq_signal(c, SLEEPQ_SLEEP, 0, 0); sleepq_release(c); if (wakeup_swapper) kick_proc0(); } /* * Indefinite wait for done != 0 with or without signals. */ long linux_wait_for_common(struct completion *c, int flags) { if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else flags = SLEEPQ_SLEEP; for (;;) { sleepq_lock(c); if (c->done) break; sleepq_add(c, NULL, "completion", flags, 0); if (flags & SLEEPQ_INTERRUPTIBLE) { if (sleepq_wait_sig(c, 0) != 0) return (-ERESTARTSYS); } else sleepq_wait(c, 0); } c->done--; sleepq_release(c); return (0); } /* * Time limited wait for done != 0 with or without signals. */ long linux_wait_for_timeout_common(struct completion *c, long timeout, int flags) { long end = jiffies + timeout; if (flags != 0) flags = SLEEPQ_INTERRUPTIBLE | SLEEPQ_SLEEP; else flags = SLEEPQ_SLEEP; for (;;) { int ret; sleepq_lock(c); if (c->done) break; sleepq_add(c, NULL, "completion", flags, 0); sleepq_set_timeout(c, linux_timer_jiffies_until(end)); if (flags & SLEEPQ_INTERRUPTIBLE) ret = sleepq_timedwait_sig(c, 0); else ret = sleepq_timedwait(c, 0); if (ret != 0) { /* check for timeout or signal */ if (ret == EWOULDBLOCK) return (0); else return (-ERESTARTSYS); } } c->done--; sleepq_release(c); /* return how many jiffies are left */ return (linux_timer_jiffies_until(end)); } int linux_try_wait_for_completion(struct completion *c) { int isdone; isdone = 1; sleepq_lock(c); if (c->done) c->done--; else isdone = 0; sleepq_release(c); return (isdone); } int linux_completion_done(struct completion *c) { int isdone; isdone = 1; sleepq_lock(c); if (c->done == 0) isdone = 0; sleepq_release(c); return (isdone); } void linux_delayed_work_fn(void *arg) { struct delayed_work *work; work = arg; taskqueue_enqueue(work->work.taskqueue, &work->work.work_task); } void linux_work_fn(void *context, int pending) { struct work_struct *work; work = context; work->fn(work); } void linux_flush_fn(void *context, int pending) { } struct workqueue_struct * linux_create_workqueue_common(const char *name, int cpus) { struct workqueue_struct *wq; wq = kmalloc(sizeof(*wq), M_WAITOK); wq->taskqueue = taskqueue_create(name, M_WAITOK, taskqueue_thread_enqueue, &wq->taskqueue); atomic_set(&wq->draining, 0); taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "%s", name); return (wq); } void destroy_workqueue(struct workqueue_struct *wq) { taskqueue_free(wq->taskqueue); kfree(wq); } static void +linux_cdev_release(struct kobject *kobj) +{ + struct linux_cdev *cdev; + struct kobject *parent; + + cdev = container_of(kobj, struct linux_cdev, kobj); + parent = kobj->parent; + if (cdev->cdev) + destroy_dev(cdev->cdev); + kfree(cdev); + kobject_put(parent); +} + +static void +linux_cdev_static_release(struct kobject *kobj) +{ + struct linux_cdev *cdev; + struct kobject *parent; + + cdev = container_of(kobj, struct linux_cdev, kobj); + parent = kobj->parent; + if (cdev->cdev) + destroy_dev(cdev->cdev); + kobject_put(parent); +} + +const struct kobj_type linux_cdev_ktype = { + .release = linux_cdev_release, +}; + +const struct kobj_type linux_cdev_static_ktype = { + .release = linux_cdev_static_release, +}; + +static void linux_compat_init(void *arg) { struct sysctl_oid *rootoid; int i; rootoid = SYSCTL_ADD_ROOT_NODE(NULL, OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys"); - kobject_init(&class_root, &class_ktype); - kobject_set_name(&class_root, "class"); - class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), + kobject_init(&linux_class_root, &linux_class_ktype); + kobject_set_name(&linux_class_root, "class"); + linux_class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class"); - kobject_init(&linux_rootdev.kobj, &dev_ktype); - kobject_set_name(&linux_rootdev.kobj, "device"); - linux_rootdev.kobj.oidp = SYSCTL_ADD_NODE(NULL, + kobject_init(&linux_root_device.kobj, &linux_dev_ktype); + kobject_set_name(&linux_root_device.kobj, "device"); + linux_root_device.kobj.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL, "device"); - linux_rootdev.bsddev = root_bus; - miscclass.name = "misc"; - class_register(&miscclass); + linux_root_device.bsddev = root_bus; + linux_class_misc.name = "misc"; + class_register(&linux_class_misc); INIT_LIST_HEAD(&pci_drivers); INIT_LIST_HEAD(&pci_devices); spin_lock_init(&pci_lock); mtx_init(&vmmaplock, "IO Map lock", NULL, MTX_DEF); for (i = 0; i < VMMAP_HASH_SIZE; i++) LIST_INIT(&vmmaphead[i]); } SYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL); static void linux_compat_uninit(void *arg) { - kobject_kfree_name(&class_root); - kobject_kfree_name(&linux_rootdev.kobj); - kobject_kfree_name(&miscclass.kobj); + linux_kobject_kfree_name(&linux_class_root); + linux_kobject_kfree_name(&linux_root_device.kobj); + linux_kobject_kfree_name(&linux_class_misc.kobj); } SYSUNINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_uninit, NULL); /* * NOTE: Linux frequently uses "unsigned long" for pointer to integer * conversion and vice versa, where in FreeBSD "uintptr_t" would be * used. Assert these types have the same size, else some parts of the * LinuxKPI may not work like expected: */ CTASSERT(sizeof(unsigned long) == sizeof(uintptr_t)); Index: projects/clang380-import/sys/compat/linuxkpi/common/src/linux_pci.c =================================================================== --- projects/clang380-import/sys/compat/linuxkpi/common/src/linux_pci.c (revision 293015) +++ projects/clang380-import/sys/compat/linuxkpi/common/src/linux_pci.c (revision 293016) @@ -1,208 +1,208 @@ /*- * Copyright (c) 2015 Mellanox Technologies, Ltd. * 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 ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #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 static device_probe_t linux_pci_probe; static device_attach_t linux_pci_attach; static device_detach_t linux_pci_detach; static device_method_t pci_methods[] = { DEVMETHOD(device_probe, linux_pci_probe), DEVMETHOD(device_attach, linux_pci_attach), DEVMETHOD(device_detach, linux_pci_detach), DEVMETHOD_END }; static struct pci_driver * linux_pci_find(device_t dev, const struct pci_device_id **idp) { const struct pci_device_id *id; struct pci_driver *pdrv; uint16_t vendor; uint16_t device; vendor = pci_get_vendor(dev); device = pci_get_device(dev); spin_lock(&pci_lock); list_for_each_entry(pdrv, &pci_drivers, links) { for (id = pdrv->id_table; id->vendor != 0; id++) { if (vendor == id->vendor && device == id->device) { *idp = id; spin_unlock(&pci_lock); return (pdrv); } } } spin_unlock(&pci_lock); return (NULL); } static int linux_pci_probe(device_t dev) { const struct pci_device_id *id; struct pci_driver *pdrv; if ((pdrv = linux_pci_find(dev, &id)) == NULL) return (ENXIO); if (device_get_driver(dev) != &pdrv->driver) return (ENXIO); device_set_desc(dev, pdrv->name); return (0); } static int linux_pci_attach(device_t dev) { struct resource_list_entry *rle; struct pci_dev *pdev; struct pci_driver *pdrv; const struct pci_device_id *id; int error; pdrv = linux_pci_find(dev, &id); pdev = device_get_softc(dev); - pdev->dev.parent = &linux_rootdev; + pdev->dev.parent = &linux_root_device; pdev->dev.bsddev = dev; INIT_LIST_HEAD(&pdev->dev.irqents); pdev->device = id->device; pdev->vendor = id->vendor; pdev->dev.dma_mask = &pdev->dma_mask; pdev->pdrv = pdrv; - kobject_init(&pdev->dev.kobj, &dev_ktype); + kobject_init(&pdev->dev.kobj, &linux_dev_ktype); kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); - kobject_add(&pdev->dev.kobj, &linux_rootdev.kobj, + kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, kobject_name(&pdev->dev.kobj)); rle = _pci_get_rle(pdev, SYS_RES_IRQ, 0); if (rle) pdev->dev.irq = rle->start; else pdev->dev.irq = 0; pdev->irq = pdev->dev.irq; mtx_unlock(&Giant); spin_lock(&pci_lock); list_add(&pdev->links, &pci_devices); spin_unlock(&pci_lock); error = pdrv->probe(pdev, id); mtx_lock(&Giant); if (error) { spin_lock(&pci_lock); list_del(&pdev->links); spin_unlock(&pci_lock); put_device(&pdev->dev); return (-error); } return (0); } static int linux_pci_detach(device_t dev) { struct pci_dev *pdev; pdev = device_get_softc(dev); mtx_unlock(&Giant); pdev->pdrv->remove(pdev); mtx_lock(&Giant); spin_lock(&pci_lock); list_del(&pdev->links); spin_unlock(&pci_lock); put_device(&pdev->dev); return (0); } int pci_register_driver(struct pci_driver *pdrv) { devclass_t bus; int error = 0; bus = devclass_find("pci"); spin_lock(&pci_lock); list_add(&pdrv->links, &pci_drivers); spin_unlock(&pci_lock); pdrv->driver.name = pdrv->name; pdrv->driver.methods = pci_methods; pdrv->driver.size = sizeof(struct pci_dev); mtx_lock(&Giant); if (bus != NULL) { error = devclass_add_driver(bus, &pdrv->driver, BUS_PASS_DEFAULT, &pdrv->bsdclass); } mtx_unlock(&Giant); return (-error); } void pci_unregister_driver(struct pci_driver *pdrv) { devclass_t bus; bus = devclass_find("pci"); list_del(&pdrv->links); mtx_lock(&Giant); if (bus != NULL) devclass_delete_driver(bus, &pdrv->driver); mtx_unlock(&Giant); } Index: projects/clang380-import/sys/conf/newvers.sh =================================================================== --- projects/clang380-import/sys/conf/newvers.sh (revision 293015) +++ projects/clang380-import/sys/conf/newvers.sh (revision 293016) @@ -1,242 +1,245 @@ #!/bin/sh - # # Copyright (c) 1984, 1986, 1990, 1993 # The Regents of the University of California. 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. # 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)newvers.sh 8.1 (Berkeley) 4/20/94 # $FreeBSD$ TYPE="FreeBSD" REVISION="11.0" BRANCH="CURRENT" if [ -n "${BRANCH_OVERRIDE}" ]; then BRANCH=${BRANCH_OVERRIDE} fi RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" if [ -z "${SYSDIR}" ]; then SYSDIR=$(dirname $0)/.. fi if [ -n "${PARAMFILE}" ]; then RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${PARAMFILE}) else RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${SYSDIR}/sys/param.h) fi b=share/examples/etc/bsd-style-copyright if [ -r "${SYSDIR}/../COPYRIGHT" ]; then year=$(sed -Ee '/^Copyright .* The FreeBSD Project/!d;s/^.*1992-([0-9]*) .*$/\1/g' ${SYSDIR}/../COPYRIGHT) else year=$(date +%Y) fi # look for copyright template for bsd_copyright in ../$b ../../$b ../../../$b /usr/src/$b /usr/$b do if [ -r "$bsd_copyright" ]; then COPYRIGHT=`sed \ -e "s/\[year\]/1992-$year/" \ -e 's/\[your name here\]\.* /The FreeBSD Project./' \ -e 's/\[your name\]\.*/The FreeBSD Project./' \ -e '/\[id for your version control system, if any\]/d' \ $bsd_copyright` break fi done # no copyright found, use a dummy if [ -z "$COPYRIGHT" ]; then COPYRIGHT="/*- * Copyright (c) 1992-$year The FreeBSD Project. * All rights reserved. * */" fi # add newline COPYRIGHT="$COPYRIGHT " # VARS_ONLY means no files should be generated, this is just being # included. if [ -n "$VARS_ONLY" ]; then return 0 fi LC_ALL=C; export LC_ALL if [ ! -r version ] then echo 0 > version fi touch version -v=`cat version` u=${USER:-root} d=`pwd` h=${HOSTNAME:-`hostname`} +v=`cat version` +u=${USER:-root} +d=`pwd` +h=${HOSTNAME:-`hostname`} if [ -n "$SOURCE_DATE_EPOCH" ]; then if ! t=`date -r $SOURCE_DATE_EPOCH 2>/dev/null`; then echo "Invalid SOURCE_DATE_EPOCH" >&2 exit 1 fi else t=`date` fi i=`${MAKE:-make} -V KERN_IDENT` compiler_v=$($(${MAKE:-make} -V CC) -v 2>&1 | grep -w 'version') for dir in /usr/bin /usr/local/bin; do if [ ! -z "${svnversion}" ] ; then break fi if [ -x "${dir}/svnversion" ] && [ -z ${svnversion} ] ; then # Run svnversion from ${dir} on this script; if return code # is not zero, the checkout might not be compatible with the # svnversion being used. ${dir}/svnversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=${dir}/svnversion break fi fi done if [ -z "${svnversion}" ] && [ -x /usr/bin/svnliteversion ] ; then /usr/bin/svnliteversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=/usr/bin/svnliteversion else svnversion= fi fi for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/p4" ] && [ -z ${p4_cmd} ] ; then p4_cmd=${dir}/p4 fi done if [ -d "${SYSDIR}/../.git" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/git" ] ; then git_cmd="${dir}/git --git-dir=${SYSDIR}/../.git" break fi done fi if [ -d "${SYSDIR}/../.hg" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/hg" ] ; then hg_cmd="${dir}/hg -R ${SYSDIR}/.." break fi done fi if [ -n "$svnversion" ] ; then svn=`cd ${SYSDIR} && $svnversion 2>/dev/null` case "$svn" in [0-9]*) svn=" r${svn}" ;; *) unset svn ;; esac fi if [ -n "$git_cmd" ] ; then git=`$git_cmd rev-parse --verify --short HEAD 2>/dev/null` svn=`$git_cmd svn find-rev $git 2>/dev/null` if [ -n "$svn" ] ; then svn=" r${svn}" git="=${git}" else svn=`$git_cmd log | fgrep 'git-svn-id:' | head -1 | \ sed -n 's/^.*@\([0-9][0-9]*\).*$/\1/p'` if [ -z "$svn" ] ; then svn=`$git_cmd log --format='format:%N' | \ grep '^svn ' | head -1 | \ sed -n 's/^.*revision=\([0-9][0-9]*\).*$/\1/p'` fi if [ -n "$svn" ] ; then svn=" r${svn}" git="+${git}" else git=" ${git}" fi fi git_b=`$git_cmd rev-parse --abbrev-ref HEAD` if [ -n "$git_b" ] ; then git="${git}(${git_b})" fi if $git_cmd --work-tree=${SYSDIR}/.. diff-index \ --name-only HEAD | read dummy; then git="${git}-dirty" fi fi if [ -n "$p4_cmd" ] ; then p4version=`cd ${SYSDIR} && $p4_cmd changes -m1 "./...#have" 2>&1 | \ awk '{ print $2 }'` case "$p4version" in [0-9]*) p4version=" ${p4version}" p4opened=`cd ${SYSDIR} && $p4_cmd opened ./... 2>&1` case "$p4opened" in File*) ;; //*) p4version="${p4version}+edit" ;; esac ;; *) unset p4version ;; esac fi if [ -n "$hg_cmd" ] ; then hg=`$hg_cmd id 2>/dev/null` svn=`$hg_cmd svn info 2>/dev/null | \ awk -F': ' '/Revision/ { print $2 }'` if [ -n "$svn" ] ; then svn=" r${svn}" fi if [ -n "$hg" ] ; then hg=" ${hg}" fi fi cat << EOF > vers.c $COPYRIGHT #define SCCSSTR "@(#)${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}" #define VERSTR "${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}\\n ${u}@${h}:${d}\\n" #define RELSTR "${RELEASE}" char sccs[sizeof(SCCSSTR) > 128 ? sizeof(SCCSSTR) : 128] = SCCSSTR; char version[sizeof(VERSTR) > 256 ? sizeof(VERSTR) : 256] = VERSTR; char compiler_version[] = "${compiler_v}"; char ostype[] = "${TYPE}"; char osrelease[sizeof(RELSTR) > 32 ? sizeof(RELSTR) : 32] = RELSTR; int osreldate = ${RELDATE}; char kern_ident[] = "${i}"; EOF echo $((v + 1)) > version Index: projects/clang380-import/sys/conf =================================================================== --- projects/clang380-import/sys/conf (revision 293015) +++ projects/clang380-import/sys/conf (revision 293016) Property changes on: projects/clang380-import/sys/conf ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/conf:r292913-293015 Index: projects/clang380-import/sys/contrib/dev/rtwn/LICENSE =================================================================== --- projects/clang380-import/sys/contrib/dev/rtwn/LICENSE (nonexistent) +++ projects/clang380-import/sys/contrib/dev/rtwn/LICENSE (revision 293016) @@ -0,0 +1,39 @@ +Copyright (c) 2010, Realtek Semiconductor Corporation +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of Realtek Semiconductor Corporation nor the names of its + suppliers may be used to endorse or promote products derived from this + software without specific prior written permission. +* No reverse engineering, decompilation, or disassembly of this software + is permitted. + +Limited patent license. Realtek Semiconductor Corporation grants a world-wide, +royalty-free, non-exclusive license under patents it now or hereafter +owns or controls to make, have made, use, import, offer to sell and +sell ("Utilize") this software, but solely to the extent that any +such patent is necessary to Utilize the software alone, or in +combination with an operating system licensed under an approved Open +Source license as listed by the Open Source Initiative at +http://opensource.org/licenses. The patent license shall not apply to +any other combinations which include this software. No hardware per +se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +COPYRIGHT OWNER 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. Index: projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU.fw.uu =================================================================== --- projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU.fw.uu (nonexistent) +++ projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU.fw.uu (revision 293016) @@ -0,0 +1,333 @@ +begin 444 rtwn-rtl8192cfwU.fw.uu +MP8@"`$@````%$!!3PCD!`(%C`0`````````````````"0[H````````````` +M`````````E!$```````````````````````````````````````````````` +M`````````````E;@```````"2VH```4$`P(``P8%!`,`!`8%!`(`!`@'!@0` +M!@H)"`8`"`H)"`0`"`H)"`(`"`H)"```"!(1$`@`$!H9&!``&"(A(!@`("(A +M(!``("(A(`@`("(A'`@`("(A%`@`("(@&`@`(#$P(!``,#$P&```,#$O$!`` +M,#$L$!``,#$H$```,#$@$```,#$0$```,`0$!`4$!`0%!04&!@0$!`4%!08& +M!`0%!04%!@8$!`4%!04&!PH+#1`$!04&!@D,$0@("0D*#!`1!`0$!00$!0<' +M!P@*!`0$!`8*"PT%!0<'"`L-#P0$!`4'!PD)#`X0$@0$!04&"A$3"0D)"0P. +M$1,````````````D)BH8&AT?(2`"@`,@!0`&0`>`",`$L`4`!X`+0`^@$L`9`!]```@`" +M``0`"``,`!(`&``D`#``2`!@`&P`*``\`%``9`"@`,@`\`$8`&0`H`#P`6@! +M]`)8`R`#Z`("`@("`@,#!`0%!P0$!PH*#`P2!0<'"`L2)#P!`0$!`0(#!`4& +M!P@!`@,$!08'"`4&!P@)"@L,(!X<&!`8````````````````NP$,Y8(I]8+E +M@SKU@^`B4`;I)8+XYB*[_@;I)8+XXB+E@BGU@N6#.O6#Y),BNP$&B8**@_`B +M4`+W(KO^`?,B^+L!#>6"*?6"Y8,Z]8/H\")0!NDE@LCV(KO^!>DE@LCR(L7P +M^*/@*/#%\/CE@A6"<`(5@^`X\"*[`0J)@HJ#X/7PH^`B4`:'\`GG&2*[_@?C +M]?`)XQDBB8**@^23]?!T`9,BNP$0Y8(I]8+E@SKU@^#U\*/@(E`)Z26"^(;P +M".8BN_X*Z26"^.+U\`CB(N6#*O6#Z9/U\*/IDR*[`0J)@HJ#\.7PH_`B4`;W +M":?P&2*[_@;SY?`)\QDB^+L!$>6"*?6"Y8,Z]8/H\.7PH_`B4`GI)8+(]@BF +M\"*[_@GI)8+(\N7P"/(B[TO_[DK^[4G][$C\(N#\H^#]H^#^H^#_(J0E@O6" +MY?`U@_6#(N#[H^#ZH^#Y(OC@^Z.CX/DE\/#E@A6"<`(5@^#Z./`BZ_"CZO"C +MZ?`BT(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3:&#OHZ.C@-\"0_@" +M2"_DDZ/XY).C0`/V@`'R"-_T@"GDDZ/X5`1^`9-@O*/_5#\PY0E4'_[DDZ-@`0[/5,`EX&"H +M0+CDDZ/ZY).C^.23H\C%@LC*Q8/*\*/(Q8+(RL6#RM_IWN>`OD&7?P!!EZ$` +M09=K@$&7HP``CX*.@Z.CH^3P(N3U(W]@?@&`[=,0KP'#P-"0EUUQBY"777%K +MD``!4<+ZY?`D`/_D.OZ0EUUQ:Y```>Z/\'$9$B1B_V`HM2,4D)==<6N0``%1 +MPF4E<`3E)&7P8!^0EUUQ:Y```5'"_Z[PL5.`#I"777%K$B1B92-@`I%8T-"2 +MKR+T_Y``0^!?\-,0KP'#P-!_$-_^T-"2KR*0`=,0KP'#P-"+8(IAB6*0EXAQBZMCJF2I99"7BW&+ +MKV859N]@&Y"7B^1U\`%Q=!(D8O^0EXCD=?`!<73O44V`WJM@JF&I8M#0DJ\B +MD)=@[O"C[_!U(P&.)/4EY/U_"[&6Y/U_`K&6$FU;Y/^1YN3U)Y`!R>4G\)"7 +M8.#\H^#][/N-1.3U17T!?V!^`0(P8M,0KP'#P-"0EV/M\)"78N_PTY0'4$ZC +MX'`:D)=BX/]T`:@'"(`"PS/8_/3_D`!'X%_P@!>0EV+@_W0!J`<(@`+#,]C\ +M_Y``1^!/\)'6D)=BX/]T`:@'"(`"PS/8_/3_D`!&@%F0EV+@)/CPH^!P'9"7 +M8N#_=`&H!PB``L,SV/S$5/#T_Y``0^!?\(`:D)=BX/]T`:@'"(`"PS/8_,14 +M\/^0`$/@3_"1UI"78N#_=`&H!PB``L,SV/ST_Y``0^!?\)'6T-"2KR+@_WT! +MD)>4[_"C[?#DH_"C\.5J8`7D_Q).>9"7E.`PX`F0EY;D\*-T@/"0!!W@8!V0 +M!2+@D)>8\.#_5)!@[)`!R'3\\.]4;Y`%(O"`W9"7E.#_PQ.0_1#PD`0E[_"0 +MEY7@8!^CH^#_)`_U@N0T_/6#X$2`\'00+_6"Y#3\]8/@1(#PD)>6H^#__20( +M]8+D-/SU@^3P=`DM]8+D-/SU@^!4\/!T(2_U@N0T_/6#X%3W\)"7EN#^H^#_ +M(O"0`$7@5/[]?T73$*\!P\#0CX)U@P#M\)'6T-"2KR+O%&`P%&!E)`)@`N': +MD)!$$/U_18!OY)"7./"0 +MES1Q4Y"`EA(E"'^`?@@2*PB0`$7@1._]?T7Q()``1>!4[_U_1?$@D`!&X$00 +M_7]&@#>0ESAT`?"0ESYQ4Y"`EA(E"'^`?@@2*PB0`$7@1"#]?T7Q()``1>!$ +M$/U_1?$@D`!&X$00_7]&\2`BD`!)X)"7I/#@5`_P1/#]?TGQ()"7I.!$L/U_ +M2>$@=2@SY/4I=2H"]2N0`3#E*/"CY2GPH^4J\*/E*_`B=3`?=3$!Y/4RD`$X +MY3#PH^4Q\*/E,O`BY)"73_"C\'6.`A)LK9```N!4X)"7AF`%=`'P@`-T`O"0 +M`//@,.,(D)>'=`'P@`7DD)>'\)"7A^"T`1.0`/+@,.<,D)=]=/WPHW0S\(`* +MD)=]=/WPHW0O\.3U5Q)JZQ)O\A)?K!(N`1)M5Q)'^9``\^`PX@V0!4%T$/"0 +M!5KPH^3PD`%D=*#P=43_Y/5%^WT!?U!^`1(P8A$7$D4"$G=(D)=1Y=GP$E`5 +MPJ^0`(#@1$#P$D36=>@#0ZB%TJ^0ET_@9`'P)"^0`<3P=$BC\.57,.0*PJ]3 +M5^_2KQ)7@.57,.86PJ]35[_2KQ)A9)"7/.#_8`.T`0(Q8)"7/.!P`Q)WIC$S +M@+B0!C3@8"8435_^7X!$FSUOP$)D`8UX%0/\(`%@``";)[DD`8T +M\"*0ES/@PY044`7@!/!!&)"7,^!D%&`"01B0ET+@<"60ET7@0!/W@5/[PD)="X)`$1/"0ET/@D`1%\)"71."0 +M!$;PH^3PD)=%X)`$2/"0ET;@D`1)\)"71^"0!$KPH^3PD)#^TY]0"Y"72^##GM.4`4`0D)"0ETGPD`5BX)"72O"0!6/@D)=+\,-T_Y_^D)=)X-.>0![@+_"CX+3_#^3P +MH^"T_P/D\"*0ETN``Y"72N`$\"*0ETG@+_`BD)#_46*0ETQT`1)'%H!`D)=,X&0!<#B0ESW@_U%BY)"73/"0`$7@1`']?T42 +M1R"0ES+@8!60ES020U.0@)82)0A_@'X($BL(@`60!2+D\)`%A^!D@/"0ETC@ +MD`6$\)"72>"0!87PD)=*X)`%AO"0ETO@D`6'\"+`X,#PP(/`@L#0==``P`#` +M`<`"P`/`!,`%P`;`!Y`!Q'1J\'1+H_!3D=^0`3S@53#U-*/@53'U-:/@53+U +M-J/@53/U-^4T,.`&D`$\=`'PY30PX0B0`3QT`O#QJ^4T,.(RD`$\=`3PD`:2 +MX##@'G5$%'5%`.3[_7]8?@$2,&*0`5MT!?"0!I)T`?"`!Y"7=N3PD8KE-##C +M,I`!/'0(\)`&DN`PX1YU1!1U10#D^_U_7'X!$C!BD`%?=`7PD`:2=`+P@`>0 +MEW7D\)&*Y30PY`F0`3QT$/`2;7?E-##E")`!/'0@\%&TY34PX!"0`3UT`?"0 +M`(/@D)=Y\)&*=&H$D`'$\'1+H_#0!]`&T`70!-`#T`+0`=``T-#0@M"#T/#0 +MX#*0EWG@_WT!@`1]`7\,CV>-:.5G5`__D)=WX%0/;V!QY6]@*[%.D)=WX%0/_[\$#>5G(.(($E8Q[V`4T920EW?@5`__ +MOP(($F`)[V`"T>!4O_`BD`8$X$1`\.5IM`$$?P'Q2)"7=^!4\/#@1`3P +M(N]D`7`N?7U_`A(Q+'T"?P,2,2R0`5?D\)`!/'0"\)&3Y/_1>9`&!.!4?_"0 +M!@K@5/CP(I`!-G1]\*-T`O!]??\2,9U]`G\#$C&=D`8$X$2`\)`&"N!$!_"0 +MEW+@H^"0!5CPY6DPX!J0EV_@%&`O)`-P0'\!@#JK$:H2J1.0 +M``(20B#]Y/_14X`GJQ&J$JD3D``"$D(@_7\!T5,?@!.K$:H2J1.0``(20B#] +M?P+14^3_L6HB[R3^8`L$V0EWAP!70%\(`"[?"0EWC@D)=M +M\"+O8`N0EX?@M`$0Y/^`"9"7A^"T`05_`1)O^R*0`3=T`O"0!2)T__`2;E_O +M<`:0`BX&`%D`4BY/"0EW?@ +M5/#PX$0$\"*0!@3@5+_P[V`)Y6FT`03D__%(D)=WX%3P\.!$#/`B$E/\OP$: +MD)=VX'`4D)=UX'`.D)=YX%0/TY0$4`-_`2)_`"*/:Y"7@A)&7>5K8!!T(2_U +M@N0T_/6#X$00\(`.="$O]8+D-/SU@^!4[_"0!!]T`?`B?0)_`Q(Q+.5J%"3] +M4`*`(9"7>N!@!GT!?PR`#Y"7=^!4#\.4!%`&?0%_!)&7Y/_1>2*0EWO@8`[D +M\*/@5/WPX%0'<">`(Y"7;N`$\)"7?.!4[_"0EV[@TY0!0`WE:;0!"J/@<`;@ +M!/`BD8HBD`$PY/"C\*/PH_"0`3CPH_"C\*/P_7]0$D<@Y/U_41)'(.3]?U(2 +M1R#D_7]3`D<@D`$\=/_PH_"C\)`!-/"C\*/PH_#]?U021R!]_W]5$D<@??]_ +M5A)'('W_?U<"1R#`X,#PP(/`@L#0==``P`#``<`"P`/`!,`%P`;`!Y`!Q'1$ +M\'10H_"0`33@52CU+)`!-N!5*O4NH^!5*_4OY2P@X`(AVY`!-'0!\(713872 +M3H733X744(7548764H774X795.545$##$__E4U0@;W`"(9+E5##E`B&2Y5)4 +M'_4(Y4U4/_4)Y5%4'__E""7@)./U@N0TE/6#Y(_P$D*!Y5-4'__E""7@),#U +M@N0TD?6#Y(_P$D*!Y0G3E`1``W4)!'7P"N4(D)``$D-?=?`"Y0D20U_@_J/@ +M_^535!\O_^0^_G7P"N4(D)``$D-?=?`"Y0D20U_N\*/O\.54(.8CY5-4'__E +M""7@)&/U@N0TE/6#Y(_P$D*!Y4\PYS2O"+'#@"[E4U0?_^4()>`DH_6"Y#24 +M]8/DC_`20H'E3S#G$>5/5'_]Y5-4'_4-JPFO")&PY6H4)/U0`H!`D)=ZX&`R +MD`%;Y/"0`3QT!/!Q_.]D`7`H=404]47[_7]8?@$2,&*0`5MT!?"0!I)T`?"0 +MEW;P@`AQ_+\!`Q),BN4L,.$FD`$T=`+PD/T0='_PA=%8A=)9A=-:A=1;A=5< +MA=9=A==>A=E?D0OE+##C!I`!-'0(\.4L,.0)D`$T=!#P0U<0Y2PPY2:0`<_@ +M,.4?X%3?\)`!-'0@\'6H`'7H`!)/Y9```^!4^_`21-:`_N4L,.8&D`$T=$#P +MY2XPX3R0`39T`O!#5T"0`0+@5`-D`7`ID`$WX##@"G0!\)"7?^3P@!B0EW_@ +M!/#@PY0*0`SD\)`$&>`PX`,21/+E+C#@")`!-G0!\-%EY2XPXG&0`39T!/#E +M:60!<&7E:F!AY6ID`F`&Y6ID!7`GD`:KX)"7;?"0!JK@D)=X\)"7;>!P!Y"7 +M>.#_@`60EVW@_Y"7;>_PD)=OX&`#X!3PD)=NY/"0`5?PD`$\=`+PD)=\X%3] +M\.!4[_#E:A0D_5`"@`*Q$.4N,.,QD`$V=`CPY6ED`7`EY6I@(9`!5^3PD`$\ +M=`+P=40#=44`Y/O]?U1^`1(P8I`!5W0%\.4N,.0OD`$V=!#PY6ED`7`CY6I@ +M'Y`!5^3PD`$\=`+PD)=[Y/"0EWS@5/WPX%0'<`,23(KE+C#E'9`!-G0@\.5I +MM`$2Y6I@#I"7>N!D`F`$L7B``K&)Y2XPYAZ0`39T0/#E:;0!$^5J8`^0EWS@ +M5/[PX%0'<`,23(KE+S#A"9`!-W0"\!)/>71$!)`!Q/!T4*/PT`?0!M`%T`30 +M`]`"T`'0`-#0T(+0@]#PT.`RD`0;X%1_9']_`6`"?P`BD)=-X%3P1`/P5`]$ +M@/![`'H`>5B0EY$20XL+>I=Y3=,0KP'#P-"0EXX20XN0EX;@9`)@;)`!K^!@ +M"9`!Q^`$\/"`\9"7H^#_!/"0EXX20VN0``'O$D)?D)>.$D-KBV.*9(EE=68" +M>P%Z`7F@$D4)D)>1$D-KBV.*9(EED)>.$D-K$B1B_\14#_5F>P%Z`7FB$D4) +MD`&O=/_PD`'+X&2`\-#0DJ\BJ07I5!_U#W0!+_6"Y#22]8/@]0Z0!/W@M`$% +M=1`#@`-U$`'KPY400`*AP^4.)0W^Y0^00=:3_>[3G70!0!0EWS@1`'PD)=ZX&0"8`2Q>(`+L8F`!Y"7?.!4_O#E"I"7?##G +M(.!$`O!U1`/D]47[_7]4?@$2,&*0`5=T!?"0EWMT`?`BX%3]\"*0EX'@_^3] +M$D9AD`0?=`'P(I`!7^3PD`$\=`CP=404Y/5%^_U_7'X!$C!BD`%?=`7PD`:2 +M=`+PD)=U%/"0EW?@5`_#E`Q0`Q),DR*M!W7P">V0DR<20U_@_W2E+?6"Y#26 +M]8/@5!_\TY]``JP'["7@))[U@N0T0?6#Y)/^=`&3_^PEX"1F]8+D-$'U@W0! +MDR__Y),^PQ/^[Q/_[27@).'U@N0TDO6#[O"C[_!TA"WU@N0T!/6#[/#_(G'\ +M[V0!<"J0EWS@5`-P(I"7>>!4#].4`E`7D)=\X"#B$)"7?.`@Y`F0EV_@<`-_ +M`2)_`"+DD)=-\.5J8''E:60!<&OE:A1@*23]8"4D`B3[4`*`(Y"7;>`4\.!@ +M!*/@8!:0EVW@<`J0EWC@D)=M\(``D)=-=`'PD)=-X&`QD)=\X$00\)"7=.#U +M1.3U1?O]?U1^`1(P8I`!5W0%\)"7=^!4#\.4!%`'?0%_!!),ER+`X,#PP(/` +M@L#0==``P`#``<`"P`/`!,`%P`;`!Y`!Q'3@\'16H_!3D>^0`%'@_Y``5>!? +M]3WE/3#F&'1`\)"7.^!4`_^_`PN0ESC@8`5_`1)'->4],.<5D`!5=(#PD)<[ +MX%0#_[\#!7\"$D0EZ'@_W0! +M?@"H!PB`!<,SSC/.V/G_[UUP`P)8O9"7H>!U\`20`=`20U_@D)=3\'5C`75D +MEW5E4W5F`7L!>I=Y5!)%"9"75.#_Q!,3$U0!D)>A,.!9X'7P`I``B!)#7^"0 +MEU7PD)>AX'7P`I``B1)#7^"0EU;PD)>AX'7P!)`!T1)#7^"0EU?PD)>AX'7P +M!)`!TA)#7^"0EUCPD)>AX'7P!)`!TQ)#7^"0EUGP@#/@=?`$D`'1$D-?X)"7 +M5?"0EZ'@=?`$D`'2$D-?X)"75O"0EZ'@=?`$D`'3$D-?X)"75_#O5'__>P%Z +MEWE5$#^=`&H!@B``L,SV/ST7Y"74O"0EZ'@_W0!J`<(@`+# +M,]C\D`',\)"7H>`$\.!4`_`"5XJ0`<;@1`+P`E>*(I"76A)#B^\20Y18]`%8 +M_`)9!`-9#`59%`991@=9'`E9)0Q9+@U9-@X``%D_D)=:$D-KP=B0EUH20VO! +MTI"76A)#:^%DD)=:$D-KX0*0EUH20VN`*Y"76A)#:P)$89"76A)#:P)X?Y"7 +M6A)#:^$QD)=:$D-K`G9PD`'&X$0!\"*0``020B#_5!_^[U0@Q!-4!_VO!I"7 +M7>_PH^WPHQ)#BY"77Q)#:Y```Q)"(%3PQ%0/D)=B\)``!!)"(%1`Q!,35`.0 +MEV/PD)==X/]U\`F0DR420U^M@JR#D)=D[/"C[?#O=?`)I"0C^723-?#Z>P&C +M$D.+D)=?$D-KD``#$D(@5`__D)=F$D-K[Q)"39"77Q)#:Y```A)"(/^0EV82 +M0VN0``'O$D)?D)=?$D-KD``!$D(@_Y"79.#\H^#]]8*,@^_P$B1BC8*,@Z/P +MD)=BX/Z0EUW@_R3!]8+D-)+U@^[PD)=>X/YU\`GOD),I$D-?[O!U\`GOD),J +M$D-?=`'PD)=CX/YU\`GOD),K$D-?[O"/$>\EX"3D]8+D-)6O@O43CQ3E$77P +M`J0D@?ETDC7P=14!]1:)%W7P">41D),E$D-?KX*%@QB/&>41=?`)I"0C^723 +M-?!U&@'U&XD<=,$E$?6"Y#22]8/@$D.46M4`6NH!6O\"6Q0#6ST$6U(%6V<& +M6XT,6[H-6^<.7!0/``!<2.41)>`DY/6"Y#25]8-T\/"C=!6`/.41)>`DY/6" +MY#25]8-T\/"C=!"`)^41)>`DY/6"Y#25]8-T\/"C=`6`$N41)>`DY/6"Y#25 +M]8-T\/"CY/#E$27@)('U@N0TDO6#=`_PHW2/\(%(Y1$EX"3D]8+D-)7U@W0/ +M\*-T]8`GY1$EX"3D]8+D-)7U@W0/\*-T\(`2Y1$EX"3D]8+D-)7U@^3PHW0- +M\.41)>`D@?6"Y#22]8/D\*/P@4B0!$?@JQ6J%JD7$D)-D`1&X*L5JA:I%Y`` +M`1)"7Y`$1>"%%(*%$X/PD`1$@3^0!$O@JQ6J%JD7$D)-D`1*X*L5JA:I%Y`` +M`1)"7Y`$2>"%%(*%$X/PD`1(@%B0!$_@JQ6J%JD7$D)-D`1.X*L5JA:I%Y`` +M`1)"7Y`$3>"%%(*%$X/PD`1,@"N0!%/@JQ6J%JD7$D)-D`12X*L5JA:I%Y`` +M`1)"7Y`$4>"%%(*%$X/PD`10X(44@H43@Z/PJQ6J%JD7P`/``L`!$B1B_ZL: +MJANI'!(D8E_0`=`"T`,20DVK%>47)`'YY#46^L`#P`+``1(D8O^K&JH;J1R0 +M``$20B!?T`'0`M`#$D)-A12"A1.#P(/`@N#_A1F"A1B#X/[O7M""T(/PA12" +MA1.#H\"#P(+@_X49@H48@Z/@_N]>T(+0@_#E$27@)('U@N0TDO6#X/ZCX$Y@ +M.W42"W0!?@"H$@B`!<,SSC/.V/G_Y1$EX"2!]8+D-)+U@^!>_J/@7TY@!N42 +M)!"`7142Y1+#E`!0RH!6Y1$EX"3D]8+D-)7U@^#^H^!.8#UU$@]T`7X`J!(( +M@`7#,\XSSMCY_^41)>`DY/6"Y#25]8/@7OZCX%].8`B0EVGE$O"`$!42Y1+# +ME`!0R(`%Y)"7:?#E$27@).3U@N0TE?6#X/ZCX$Y@.^3U$G0!?@"H$@B`!<,S +MSC/.V/G_Y1$EX"3D]8+D-)7U@^!>_J/@7TY@")"7:N42\(!;!1+E$K00RH!2 +MY1$EX"2!]8+D-)+U@^#^H^!.8#GD]1)T`7X`J!((@`7#,\XSSMCY_^41)>`D +M@?6"Y#22]8/@7OZCX%].8`;E$B00@`H%$N42M`S,@`7DD)=J\)"7:>#_=?`) +MY1&0DR<20U_O\)"7:N#^=?`)Y1&0DR@20U_N\'2$)1'U@N0T!/6#X-.?0!^0 +MEVG@_W2$)1'U@N0TEO6#[_!TA"41]8+D-`3U@^_P=(0E$?6"Y#0$]8/@PYY0 +M'Y"7:N#_=(0E$?6"Y#26]8/O\'2$)1'U@N0T!/6#[_"0EVG@_].4$T`(D),B +M=`/P@"'OTY0+0`B0DR)T`O"`$^_3E`-`")"3(G0!\(`%Y)"3(O"0DR+@D`2Q +M\"(2)&+U:2+3$*\!P\#0D``!$D(@D)=Z\)```Q)"()"7;/`2)&)E:F`#$DWQ +MT-"2KR(2)&+U$<.4(%`5D``"$D(@_W0C)1'U@N0TE?6#[_`BY1&T(`J0``(2 +M0B"0DR'P(I```A)"()"7//#@8`3@]'`AHJ_D,_41PJ^0`$?@5/O]?T<21R!] +M0'\!$C%FY1$D_Y*O(I`"">#]$B1B_J\%[2Z0EX#PD``!$D(@_^TOD)>!\)`` +M`A)"(/_M+Y"7@O"0``,20B#_[2^0EX/PD``$$D(@_ZX%[2^0EX3P(N3U:9"7 +M?/#U:I"7>70,\)"7=_#DD)=Z\)"7=O"0EW7PD)=X!/"0EVWPY)"7>_"0EV_P +MD)=T=`?PY)"7;O"0EW+PHW0"\.20EW'PD)=L\"+DD)=[\)"7;O"0EWSP(N57 +M<#>0EWG@5`_3E`%0+)`"A^!P)I"7AN"T`A"0EWW@_J/@]8*.@^!@"(`/D`&O +MX'`)D)=QX&`#?P$B?P`BD``KX$0!\'_H?@,2,A60``C@1!#]?P@21R"0``G@ +M5/?]?PD21R"0`"C@5/[]?R@21R"0`"#@5/[]?R`21R"0`"7@1$#]?R421R"0 +M``G@5._]?PD"1R"0`"7@5+_]?R421R"0`"#@1`']?R`21R"0`"C@1`']?R@2 +M1R"0`/#@,.'YD``)X$0(_7\)$D<@D``(X%3O_7\($D<@D``KX%3^_7\K$D<@ +M?^A^`P(R%8]LD)>#$D9=Y6Q@$'0A+_6"Y#3\]8/@1!#P@`YT(2_U@N0T_/6# +MX%3O\)`$'W0!\"+OPY0@4#GO,.`7[<14\/WOPQ/^)*3U@N0T!/6#X%0/@!#O +MPQ/^)*3U@N0T!/6#X%3P\'2D+O6"Y#0$]8/@3?`BY/41=?`)Y1&0DRH20U_@ +M9`%@`N%AY1$EX"3`]8+D-)'U@^#^H^#3E`#NE`!0`N%AY1%U\`JD)`#Y=)`U +M\'46`?47B1CE$27@),#U@N0TD?6#X/^CX)"75L_PH^_PY1$EX"1C]8+D-)3U +M@^#_H^"0EUC/\*/O\'2$)1'U@N0T!/6#X%0_D)=2\.#^5!^C\'7P">41D),G +M$D-?X)"76_!T9"41]8+D-);U@^##E`5``H$[D)=;X/^0EU/@GT`3D)=;X)"7 +M4_#N5$#^D)=2\.].\)`$_>!D`7`ID)=3X/^004J3_G0C)1'U@N0TE?6#X,.> +M0`;OD$#:@#"0EU/@D$#V@">0EU/@_Y!!2I/^=",E$?6"Y#25]8/@PYY`!N^0 +M01*`!Y"74^"002Z3D)=:\)"76N!U\`:D)%#Y=$`U\'43__44B160EU+@D$'R +MD__3D)=9X)^0EUC@E`!`">3]KQ$2:5K!^.41)>`DX?6"Y#22]8/@]1FCX/4: +MJQ.J%*D5$B1B_WX`JQ:J%ZD8$D*7_:SP$B1[[R4:]1KN-1GU&:L3JA2I%9`` +M`1)"(/]^`*L6JA>I&)```A)"POVL\!(D>^\E&O4:[C49]1FK$ZH4J160``(2 +M0B#_?@"K%JH7J1B0``020L+]K/`2)'OO)1KU&NXU&?49JQ.J%*D5D``#$D(@ +M_WX`JQ:J%ZD8D``&$D+"_:SP$B1[[R4:]1KN-1GU&:L3JA2I%9``!!)"(/]^ +M`*L6JA>I&)``"!)"POVL\!(D>^\E&O4:[C49]1FK$ZH4J160``420B#_?@"0 +MEU;@_*/@_1(D>]/E&I_E&9Y`#.4:G_4:Y1F>]1F`!>3U&?4:Y1$EX"3A]8+D +M-)+U@^49\*/E&O"0EU+@)>`D9O6"Y#1!]8/#=`&3E1KDDY494`:O$?%MP`DGO6"Y#1!]8/3=`&3E1KDDY494`+!S'T!KQ$2:5K!S'1D)1'U@N0T +MEO6#X/QD!6`"H=:0DR+@_[0#"Y"74^##E!E`/8`N[[0""Y"74^##E!%`+H`? +MD),BX/^T`0N0EU/@PY0*0!N`#.]P$9"74^##E`-`#9"50W0!\(`%Y)"50_!T +M0R41]8+D-)3U@^#U&W0C)1'U@N0TE?6#X/_#E#!0`J&#D)5#X&0!8`*A@W1$ +M)1'U@N0TE?6#X&0*8%'O)`7_Y#/^="$E$?6"Y#22]8/@_=.?[F2`^'2`F%`R +M[20%_^0S_G0C)1'U@N0TE?6#X-.?[F2`^'2`F%`4=(0E$?6"Y#26]8/@_Y"7 +M4^!O8#UT(R41]8+D-)7U@^#_TY1"0`5U&P6`#N_3E#E`!74;`X`#=1L!="$E +M$?6"Y#22]8/O\'1$)1'U@N0TE8`I=&0E$?6"Y#26]8/D\'1$)1'U@N0TE?6# +MX`3P@!#D]1MT9"41]8+D-);U@^3PD)=3X/]TA"41]8+D-);U@^_P=$,E$?6" +MY#24]8/E&_!U\`GE$9"3*Q)#7^"T`1#D]1MT9"41]8+D-);U@^3PK1O!R.QD +M!F`"PI&'7P`N42I/6"A?"#$D+"_:SPY1*00@Z3_WX`$B1[[R4: +M]1KN-1GU&<.0EU7@E1J0EU3@E1E`!P42Y1*T!;WE$L,3]1+E&[0!!N42<$:` +M$^4;M`,5Y1)P!74;`X`YY1*T`05U&P&`+X`JY1NT!2CE$G`%=1L%@`WE$K0! +M!74;`X`#=1L!TY"76>"4`Y"76."4`$`#Y/4;TY"76>"4`Y"76."4`$`#Y/4; +M=$,E$?6"Y#24]8/E&_#]KQ$Q)'1D)1'U@N0TEO6#X-.4!71D4`XE$?6"Y#26 +M]8/@!/"`"R41]8+D-);U@^3PJQ:J%ZD8Y/7P$D+ZJQ:J%ZD8D``"Y/7P$D,9 +MD``$Y/7P$D,9D``&Y/7P$D,9D``(Y/7P$D,9Y1$EX"3`]8+D-)'U@^3PH_#E +M$27@)&/U@N0TE/6#Y/"C\.41)>`DH_6"Y#24]8/D\*/P!1'E$<.4(%`"(6S#E!!`(>PD\/]T`7X`J`<(@`7#,\XSSMCY +M_Y"77>!>_J/@7TYP(^S#E!!0.70!?@"H!`B`!<,SSC/.V/G_D)=?X%[^H^!? +M3F`<[&038`CL9!)@`[P1"9"77>`PX`)\&*T$C1R`-`R`BY"77.#\;7!I=*4I +M]8+D-);U@^WP=?`)Z9"3*1)#7^"T`0SE'"#F!^U$0/4<@`.O'"+M)>`DGO6" +MY#1!]8/DD_YT`9/_[27@)&;U@N0T0?6#=`&3+__DDS[#$_[O$__I)>`DX?6" +MY#22]8/N\*/O\(!;[=.<0%:0EUS@_W2E*?6"Y#26]8/O\*T'CQSM)>`DGO6" +MY#1!]8/DD_YT`9/_[27@)&;U@N0T0?6#=`&3+__DDS[#$_[O$__I)>`DX?6" +MY#22]8/N\*/O\*\<(G0!*?6"Y#22]8/D\.4<1(#_=(0I]8+D-`3U@^_P(JH' +M=(0J]8+D-`3U@^!4?_M4'_F0EU[P=?`)ZI"3*!)#7^"0EV#P=?`)ZI"3)Q)# +M7^"0EV'P_.HEX"3D]8+D-)7U@^#_H^"0EV+/\*/O\.HEX"2!]8+D-)+U@^#_ +MH^"0EV3/\*/O\.G3G$`)D)=AX)"77O#[[7`"09V0EU_M\.LPY@F0EU[@^Z/@ +M%/"0EU_@<`)!G9"77N#_TY0`4`)!G>20EUWP[Q20EUSPD)=@X/F0EUS@_M.9 +M0'+NE!!`)>XD\/]T`7X`J`<(@`7#,\XSSMCY_Y"79.#\H^#][%[^[5].<"N0 +MEUS@_\.4$%`V=`%^`*@'"(`%PS/.,\[8^?^0EV+@_*/@_>Q>_NU?3F`5D)=< +MX/NCX`3PD)=?X/^0EUW@;V`(D)=##GU`,D)=`DX?6"Y#22]8/N\*/O\'2$*O6"Y#0$]8/K\/\BD`1$=!'P +MHW3P\*-T#_"CY/#]=*0M]8+D-`3U@^3P#;T0\.3]=?`*[9"0`!)#7^3PH_!U +M\`KMD)`"$D-?Y/"C\'7P"NV0D`020U_D\*/P=?`*[9"0!A)#7^3PH_!U\`KM +MD)`($D-?Y/"C\'2$+?6"Y#26]8-T$_!T1"WU@N0TE?6#Y/!T0RWU@N0TE/6# +MY/#M)>`DP/6"Y#21]8/D\*/P[27@)&/U@N0TE/6#Y/"C\.TEX"3C]8+D-)3U +M@^3PH_#M)>`DH_6"Y#24]8/D\*/P[27@)&3U@N0TE?6#Y/"C\.TEX"2D]8+D +M-)7U@^3PH_!T1"WU@N0TEO6#Y/!T)"WU@N0TEO6#Y/!T9"WU@N0TEO6#Y/"0 +M0<23_G0!D_^008QT`9,O_^23/L,3_N\3_^TEX"3A]8+D-)+U@^[PH^_P=?`) +M[9"3*A)#7W0!\'7P">V0DRD20U]T`?!TP2WU@N0TDO6#=`SP=?`)[9"3)1)# +M7W3_\*/P=?`)[9"3(Q)#7^3PHW0/\'7P">V0DR<20U]T$_!U\`GMD),H$D-? +MY/!TA"WU@N0T!/6#=!/P#>UD(&`"80\BD`8T=/_PY*/PH_"C\"(BY)"7A?"B +MKS.0EU/PD`"`X"#A&A(R*Q(R*Y"74N!D`?#@)*V0`<3P=&RC\(#?D`8P=`'P +MPJ^0`(#@1(#P$D36D)=3X"3_DJ\BCA&/$HL3BA2)%>20EU+P[Y``,?`21-;E +M$50#_Y``,N!4_$_P$D36D``SX%1_\!)$UI``,^`@YPZ0EU+@PY1D4`7@!/"` +MZY"74N##E&10$)``,."K$ZH4J1420DU_`2)_`"+D]28B?PNQON]E)F`0Y2:T +M`07D]2:``W4F`7\!(G\`(N4C9`%P0+%;OP$%?P$21.:0`$;@1`3]?T821R"0 +M`$3@5/O]?T021R"0`$;@5/O]?T821R!_`K&^CR>0`_PTY0'4$?@_W0!J`<(@`+#,]C\]/^0`$;@7_`21-:0EZ7@_70! +M?@"H!0B`!<,SSC/.V/G_D`!$X/OD_N];J`4(@`;.HN<3SA/8^/^`1)"7I>`D +M^/#@_W0!J`<(@`+#,]C\$D3.D)>EX/UT`7X`J`4(@`7#,\XSSMCY_Y``0N#[ +MY/[O6Z@%"(`&SJ+G$\X3V/C_T-"2KR+DD)>=\*/PD`7XX'`/H^!P"Z/@<`>C +MX'`#?P$BTY"7GN"4Z)"7G>"4`T`#?P`B?S)^`!(R%9"7G>1U\`$20H&`QI`` +M$>!$"?`21-:0EQT20U.0@)82)0A_>'X($BL(D)3]_Q(P+)"7A^"T`1&0@&@2)10``RV5Y/U_`1(P+")_>'X($B)ED)<= +M$B4(?P1^#!(B99"7(1(E"'\`?@@2(F60ER42)0B0EX?@D)<=M`$-$D-3[U3' +M_^U4Q_V`!Q)#4^]4Q__LD("6$B4(?WA^"!(K")"7(1)#4^]4#__LD("6$B4( +M?P1^#!(K")"7)1)#4^]$`O_LD("6$B4(?P!^"!(K"']P?@X2(F60ERD2)0B0 +M@)82)10`&R6@?W!^#A(K")"`:!(E%`````#D_?\2,"R0EX?@M`$1D(!H$B44 +M`````.3]?P$2,"R0`!'@5/;P`D36D)>'X)"7+?`B[W`"(;&0ERW@8`*A?)"7 +M&1)#4Y"`EA(E"'^,?@@2*PB0EL420U.0@)82)0A_1'X($BL(D);)$D-3D("6 +M$B4(?UQ^"!(K")"6S1)#4Y"`EA(E"']L?@X2*PB0EM$20U.0@)82)0A_<'X. +M$BL(D);5$D-3D("6$B4(?W1^#A(K")"6V1)#4Y"`EA(E"']X?@X2*PB0EMT2 +M0U.0@)82)0A_?'X.$BL(D);A$D-3D("6$B4(?X!^#A(K")"6Y1)#4Y"`EA(E +M"'^$?@X2*PB0END20U.0@)82)0A_B'X.$BL(D);M$D-3D("6$B4(?XQ^#A(K +M")"6\1)#4Y"`EA(E"'_0?@X2*PB0EO420U.0@)82)0A_U'X.$BL(D);Y$D-3 +MD("6$B4(?]A^#A(K")"6_1)#4Y"`EA(E"'_9$B4(D)>9$D-3[43`_>R0EYD2)0B0EYD20U.0@)82)0A_C'X( +M$BL(D("6$B44``$``']$?@@2*PB0@)82)10`VR6D?UQ^"!(K")"`EA(E%"#; +M):1_;'X.$BL(D("6$B44(-LEI']P?@X2*PB0@)82)10$&R6D?W1^#A(K")"` +MEA(E%`0;):1_>'X.$BL(D("6$B44!!LEI']\?@X2*PB0@)82)10$&R6D?X!^ +M#A(K")"`EA(E%&/;):1_A'X.$BL(D("6$B44!!LEI'^(?@X2*PB0@)82)10@ +MVR6D?XQ^#A(K")"`EA(E%"#;):1_T'X.$BL(D("6$B44(-LEI'_4?@X2*PB0 +M@)82)10@VR6D?]A^#A(K")"`EA(E%``;):1_W'X.$BL(D("6$B44`!LEI'_@ +M?@X2*PB0@)82)10DVR6D?^Q^#A(K"'\$?@P2(F60EYD2)0B0EYD20U/D_^R0 +MEYD2)0B0EYD20U/O1!'_[)"7F1(E")"7F1)#4Y"`EA(E"'\$?@P2*PA_!'X- +M$B)ED)>9$B4(D)>9$D-3[U3P_^R0EYD2)0B0EYD20U/O1`'_[)"7F1(E")"7 +MF1)#4Y"`EA(E"'\$?@T2*PA_#'X)$B)ED)>9$B4(D)>9$D-3Y/_LD)>9$B4( +MD)>9$D-3[T01_^R0EYD2)0B0EYD20U.0@)82)0A_#'X)$BL(?PQ^"1(B99"7 +MF1(E")"7F1)#4^U4#_WL5/#\D)>9$B4(D)>9$D-3[400_>Q$`?R0EYD2)0B0 +MEYD20U.0@)82)0A_#'X)$BL(?P1^"!(B99"7F1(E")"7F1)#4^]4\/_LD)>9 +M$B4(D)>9$D-3[T0!_^R0EYD2)0B0EYD20U.0@)82)0A_!'X($BL(Y)"7+?`B +MTQ"O`?[_#3E`=09>#_=`&H!PB``L,SV/ST_Y``1^!?\!)$ +MUI"7G^#_=`&H!PB``L,SV/S_D`!&X$_P$D36D)>@X&`6D)>?X/]T`:@'"(`" +MPS/8_/^0`$6`:)"7G^#_=`&H!PB``L,SV/ST_Y``18!MD)>?X"3X\.#_=`&H +M!PB``L,SV/S$5/`21,Z0EY_@_W0!J`<(@`+#,]C\_Y``0^!/\!)$UI"7H.!@ +M&Y"7G^#_=`&H!PB``L,SV/S$5/#_D`!"X$^`&I"7G^#_=`&H!PB``L,SV/S$ +M5/#T_Y``0N!?\!)$UM#0DJ\BBQ&*$HD3D``"$D(@D)<[\.`PX$N0ES)T`?!_ +M@'X($B)ED)D`!'X%3S_7]'$D<@D`!(X%3S_7]($D<@D`!&X%3O_7]& +M$D<@Y)"7./`BY/U_11)'()`$_>3PH_"0ESSPD)="\)"71?"0ET/PD)=&\)"7 +M1/"0ET?PD)!$P/U_40)'()"73.!D`6`)D)`$\(`DD)=#X,.4 +M_U`&X`3PY(`1D)=$X,.4_U`,X`3PY)"70_"0ET+PD`!$X##C,I"71>##E/]0 +M!>`$\(`DD)=&X,.4_U`&X`3PY(`1D)='X,.4_U`,X`3PY)"71O"0ET7PD`3] +MX$0!\"*0``(20B"0ESKPD``!$D(@)>`EX)"7.?`2)&(EX"7@D)<]\)`%8."0 +METCPD`5AX)"72?"0!6+@D)=*\)`%8^"0ETOPHJ_D,Y"77?#"KY"7.>#_$DIB +MD)==X"3_DJ^0ESK@<`(AB9"7.>!P`B&)D)<]X'`"(8FBK^0SD)==\,*OD)=, +M=`'PD)==X"3_DJ\21Q>0`$;@1`']?T821R"0ES+@8!60ESX20U.0@)82)0A_ +M@'X($BL(@`:0!2)T?_"0`$7@5._]?T421R"0!8?@9(#PD)=(X)`%A/"0ETG@ +MD`6%\)"72N"0!8;PD)=+X)`%A_"BK^0SD)==\,*OD`$\X$0@\'T@Y/\2,;>` +M+9"7.N!P+Y"73!)'%I``1N!4_OU_1A)'()`%(N3PHJ\SD)==\,*O?2#D_Q(Q +-29"77>`D_Y*O(@"'%P`` +` +end Index: projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU_B.fw.uu =================================================================== --- projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU_B.fw.uu (nonexistent) +++ projects/clang380-import/sys/contrib/dev/rtwn/rtwn-rtl8192cfwU_B.fw.uu (revision 293016) @@ -0,0 +1,366 @@ +begin 444 rtwn-rtl8192cfwU_B.fw.uu +MPH@"`%@``@`'%11$K#\!`,S,S,P````````````````"19L````````````` +M`````````EE4```````````````````````````````````````````````` +M`````````````F2,```````"2[(```4$`P(``P8%!`,`!`8%!`(`!`@'!@0` +M!@H)"`8`"`H)"`0`"`H)"`(`"`H)"```"!(1$`@`$!H9&!``&"(A(!@`("(A +M(!``("(A(`@`("(A'`@`("(A%`@`("(@&`@`(#$P(!``,#$P&```,#$O$!`` +M,#$L$!``,#$H$```,#$@$```,#$0$```,`0$!`4$!`0%!04&!@0$!`4%!08& +M!`0%!04%!@8$!`4%!04&!PH+#1`$!04&!@D,$0@("0D*#!`1!`0$!00$!0<' +M!P@*!`0$!`8*"PT%!0<'"`L-#P0$!`4'!PD)#`X0$@8'"0H,#A$3"0D)"0P. +M$1,````````````D)BH8&AT?(2`"@`,@!0`&0`>`",`$L`4`!X`+0`^@$L`9`!]```@`" +M``0`"``,`!(`&``D`#``2`!@`&P`*``\`%``9`"@`,@`\`$8`&0`H`#P`6@! +M]`)8`R`#Z`("`@("`@,#!`0%!P(#!`H,#A`2!0<'"`L2)#P!`0$!`0(#!`4& +M!P@!`@,$!08'"`4&!P@)"@L,(!X<&!`8````````````````NP$,Y8(I]8+E +M@SKU@^`B4`;I)8+XYB*[_@;I)8+XXB+E@BGU@N6#.O6#Y),BNP$&B8**@_`B +M4`+W(KO^`?,B^+L!#>6"*?6"Y8,Z]8/H\")0!NDE@LCV(KO^!>DE@LCR(L7P +M^*/@*/#%\/CE@A6"<`(5@^`X\"*[`0J)@HJ#X/7PH^`B4`:'\`GG&2*[_@?C +M]?`)XQDBB8**@^23]?!T`9,BNP$0Y8(I]8+E@SKU@^#U\*/@(E`)Z26"^(;P +M".8BN_X*Z26"^.+U\`CB(N6#*O6#Z9/U\*/IDR*[`0J)@HJ#\.7PH_`B4`;W +M":?P&2*[_@;SY?`)\QDB^+L!$>6"*?6"Y8,Z]8/H\.7PH_`B4`GI)8+(]@BF +M\"*[_@GI)8+(\N7P"/(B[TO_[DK^[4G][$C\(N#\H^#]H^#^H^#_(J0E@O6" +MY?`U@_6#(N#[H^#ZH^#Y(OC@^Z.CX/DE\/#E@A6"<`(5@^#Z./`BZ_"CZO"C +MZ?`BT(/0@OCDDW`2=`&3<`VCHY/X=`&3]8*(@^1S=`*3:&#OHZ.C@-_0@]"" +M^.23]D`6`(D`&Y=`'P@&[E)50#8`B0`;ET`O"`8.4C5`_3 +ME`)`")`!N70$\(!/Y24PX@B0`;ET"/"`0N4E,.0(D`&Y=!#P@#60DE?@8`B0 +M`;ET(/"`)Y"27.!@")`!N72`\(`9Y29@!Y`!N>3P@`Z0`;GD\)`!N'0$\'\! +M(I`!N'0$\'\`(@)%`P)%!GT!?PS3$*\!P\#0CV>-:.5G5`__Y2)4#V]@4B5`__OP0.Y6<@X@D21'SO +M8!022C/E(E0/_[\""1)+@N]@`Q)+,=#0DJ\B`D79`E%WY).C^.23HT`#]H`! +M\@C?](`IY).C^%0')`S(PS/$5`]$(,B#0`3T5H`!1O;?Y(`+`0($"!`@0("0 +M2T3D?@&38+RC_U0_,.4)5!_^Y).C8`$.SU3`)>!@J$"XY).C^N23H_CDDZ/( +MQ8+(RL6#RO"CR,6"R,K%@\K?Z=[G@+[D_^4D8'7E(60!<&_E)!1@*R3]8"`4\.!@!*/@8!20DE7@<`B0DF/@D))5\'\!@`)_`>]@.D,E +M$.20DH/PD))6X'7P`Z3_D))?X"^0DH3PY/O]?U1^`1)+EY`!5W0%\.4B5`_# +ME`10!WT!?P0210TBY2%D`7!GY21@8^4D9`)@!N4D9`5P)Y`&J^"0DE7PD`:J +MX)"28_"0DE7@<`>0DF/@_X`%D))5X/^0DE7O\)"25^!@`N3PY)"25O"0!5AT +M`_"0`5?D\)`!/'0"\%,E_5,E[^4D%"3]4`*``Q)'""+DD)(0\)`&J>"0DA#P +MX%3`<`E3)?Y3)?T22WN0DA#@,.850R4!D))DX&0"8`422LR`"!))\X`#4R7^ +MD)(0X##G)T,E`N20DH/PD));X)"2A/#D^_U_5'X!$DN7D`%7=`7PD))E=`'P +M(E,E_2+O9`%P,'UX?P(2-G5]`G\#$C9UD`%7Y/"0`3QT`O`210GD_Q)8II`& +M!.!4?_"0!@K@5/CP(I`!-G1[\*-T`O!]>_\2-N9]`G\#$C;FD`8$X$2`\)`& +M"N!$!_`22W#E(2#@!>20DE?P(I"2*1)#BQ)+9)"2*1)#:Q(IV?4D%&`.%&`? +M%&`Q)`-P1'\!@#V0DBD20VN0``(20B#]Y/\22F6`*9"2*1)#:Y```A)"(/U_ +M`1)*91^`%)"2*1)#:Y```A)"(/U_`A)*9>3_$D=T(N3U)?4D=2,,=2(,D))D +M\)"28O"0DF'PD))C!/"0DE7PY)"29?"0DE?PD))?=`7PY)"25O"0DEWPHW0# +M\)"26O"C=`7PD))9=!3PD))@=`7PY)"26/"0DE3PD))]\)"27/`B$DM5[V0! +M8`B0`;ET`?"`1Y"28N!@")`!N70"\(`YD))AX&`(D`&Y=`3P@"OE(U0/TY0$ +M0`B0`;ET"/"`&N4F8`B0`;ET(/"`#I`!N>3PD`&X=`CP?P$BD`&X=`CP?P`B +MY)"2%O#E)&!)D))EX&`-Y/!3)?WE)50'<#B`,Y"25N`$\%,E[Y"2%N#_D)): +MX"__Y#/^D))6X-.?[F2`^'2`F$`-Y2&T`0NCX'`'X`3P(A)+>R+E)!0D_5`" +M@$>0DF3@8"P210GDD)*#\)"26>"0DH3PY/O]?UA^`1)+EY`!6W0%\)`&DG0! +M\)"28O"`$.4B5`_#E`10!WT!?P0210WD_Q)8IB+3$*\!P\#0CV.0!!W@8"20 +M!2+@]69T__`2=F6_`0V0DGC@_WT!$E@*$D/GD`4BY6;P@`V0DGC@_WT!$E@* +M$D/GD`0?="#PT-"2KR*0`5_D\)`!/'0(\.20DH/PD))9X)"2A/#D^_U_7'X! +M$DN7D`%?=`7PD`:2=`+PD))A%/#E(E0/PY0,4`,210DBD`$W=`+PD`4B=/_P +M$G9E[W`&D`'(=/WP?0)_`Q(VYN4D8`5_`1)8IA)W&U,B\$,B`B+O)/Y@"P1P +M(I"28W0!\(`6[7`*D))@X)"28_"`!9"28^WPD))CX)"25?`B[V`/="$M]8+D +M-/SU@^!$$/`B="$M]8+D-/SU@^!4[_`BD`8$X%2_\.]@"N4AM`$%Y/\22:A3 +M(O!#(@PBD`0=X'`4D))WX/_D_1)8"HYICVJ0!!]T(/`BD)*I[_`2=J:0DJG@ +M8`60!2+D\%,B\$,B!"*0!@3@1$#PY2&T`05_`1))J%,B\$,B!"+E(S#F$N4C +M5`__D`$OX%2`3V2`\%,COR)3(O!#(@$22X422X93(O!#(@(B09)U`$&2J`!! +MDJH`09*B``"0!!O@5']D?W\!8`)_`"+DD))E\)"25O#U)2*0DEW@H^"0!5CP +M(GT!KR,"10U_`"(B(O"0DEG@D)*$\.3[_7]8?@'3$*\!P\#0D)*#X/NCX/5$ +MY/5%$C6KT-"2KR+`X,#PP(/`@L#0==``P`#``<`"P`/`!,`%P`;`!W4.`%.1 +MWY`!/.!5,/4TH^!5,?4UH^!5,O4VH^!5,_4WY30PX`:0`3QT`?#E-##A")`! +M/'0"\#$$Y30PXBR0`3QT!/#E)&`BD`:2X##@%)"2@^1QAY`!6W0%\)`&DG0! +M\(`'D))BY/!Q>^4T,.,\D`$\=`CPY21@,I`&DN`PX220DH/D\)"26>"0DH3P +MY/O]?UQ^`7&7D`%?=`7PD`:2=`+P@`>0DF'D\'%[Y30PY`B0`3QT$/"1]N4T +M,.4)D`$\="#P$E._Y34PX!:0`3UT`?"0`(/@]2.0`;OE(_!Q&7%[Y34PX@:0 +M`3UT!/#E-3#D!I`!/700\.4V,.`&D`$^=`'PY38PX0:0`3YT`O#0!]`&T`70 +M!-`#T`+0`=``T-#0@M"#T/#0X#+E7F0!<#OQN[\!!'\!\:^0`$;@1`3]?T:Q +M0I``1.!4^_U_1+%"D`!&X%3[_7]&L4)_`O'ZCV*0`20DC_PD)([$D-3 +MD("%$BI_?X!^"!(OV9``1>!$[_U_1;%"D`!%X%3O_7]%L4*0`$;@1!#]?T:` +M.)"2/W0!\)"211)#4Y"`A1(J?W^`?@@2+]F0`$7@1"#]?T6Q0I``1>!$$/U_ +M1;%"D`!&X$00_7]&L4(BD``"$D(@D))!\)```1)"("7@)>"0DD#P$BG9)>`E +MX)"21/"0!6#@D))/\)`%8>"0DE#PD`5BX)"24?"0!6/@D))2\**OY#.0DB7P +MPJ^0DD#@_Q)3;9"2)>`D_Y*OD))!X'`"X0:0DD#@<`+A!I"21.!P`N$&HJ_D +M,Y"2)?#"KY"24W0!\)"2)>`D_Y*OL3F0`$;@1`']?T:Q0I"2.>!@%9"211)# +M4Y"`A1(J?W^`?@@2+]F`!I`%(G1_\)``1>!4[_U_1;%"D`6'X&2`\)"23^"0 +M!83PD))0X)`%A?"0DE'@D`6&\)"24N"0!8?PHJ_D,Y"2)?#"KY`!/.!$(/!] +M(.3_$C<`@"N0DD'@<"V0DE.Q.)``1N!4_OU_1K%"D`4BY/"BKS.0DB7PPJ]] +M(.3_$C:2D)(EX"3_DJ\BD`$\=/_PH_"C\)`!-/"C\*/PH_#]?U2Q0GW_?U6Q +M0GW_?U:Q0GW_?U>A0I`!,.3PH_"C\*/PD`$X\*/PH_"C\/U_4+%"Y/U_4;%" +MY/U_4K%"Y/U_4Z%"D`!)X)"2J_#@5`_P1/#]?TFQ0I"2J^!$L/U_2:%"D`'* +MY6'P[V`"\9$B?POQ^N]E86`0Y6&T`07D]6&``W5A`7\!(G\`(N20DGOPD`"` +MX$2`_7^`H4+@7_#3$*\!P\#0?Q#?_M#0DJ\BTQ"O`0DJS@_70!?@"H!0B`!<,SSC/.V/G_D`!$ +MX/OD_N];J`4(@`;.HN<3SA/8^/^`3)"2K.`D^/#@_W0!J`<(@`+#,]C\]/^0 +M`$/@7_`23^J0DJS@_70!?@"H!0B`!<,SSC/.V/G_D`!"X/OD_N];J`4(@`;. +MHN<3SA/8^/_0T)*O(I`"A._PH^[PHW0%\"+OCO`20[I0RP!`4/,`@%$>`0!1 +M,@(`44H$````46?M5#]P!/[_@`1^`']`[RW_[CS^[W@&SL,3SA/8^7@&PS/. +M,\[8^8`F[51_<`3^_X`$?@!_@.\M_^X\_N]X!\[#$\X3V/EX!\,SSC/.V/G] +MK`:`2>UP!/[_@`1^`7\`[RWN/'T`_(`U[%0!37`$_O^`!'X"?P#O+>X\PQ-] +M`(`:[%0#37`$_O^`!'X$?P#O+>X\$Q-4/WT`)>`EX/RN!*\%(I`!Y'18\*-T +M`O`BY)"2%_"C\'6.`A)/U]&ED))\[_#1F9"2?N_PT>V0DG/N\*/O\.3U5?4A +M$G#6T9`22$(2,CW1A1)/.O$%T<#1B='5T811)Q)^=)"2&>79\,*OD`"`X$1` +M\!)/ZG7H`T.HA=*O,6R0DA?@9`'PY54PY`G"KU-5[]*OD77E53#F%L*O4U6_ +MTJ\2:;R0DD/@_V`#M`$"46N0DD/@<`,2?M)1/Y`!O>4B\)"29."0`;SP@+60 +MDJ/@5/[PY)"2I?"0DJ/@5'_PHW0*\"*0!C3@8"4435_^7X!$G8# +MOP$)D`8UX%0/\(`$@`#!LN20!C3P(I"2.N##E!10!>`$\&$CD)(ZX&048`)A +M(Y"22>!P)9"23.!P'Y"22N!P&9"23>!P$Y"22^!P#9"23N!P!Y`$_>!4_O"0 +MDDG@D`1$\)"22N"0!$7PD))+X)`$1O"CY/"0DDS@D`1(\)"23>"0!$GPD)). +MX)`$2O"CY/"0DC7@D`1,\)"2-N"0!$WPD)(WX)`$3O"0DCC@D`1/\.20DCKP +MD)(U!/#DH_"C\*/PD)))\*/PH_"C\*/PH_"0!6#@D)(:\)`%8>"0DAOPD`5B +MX)"2'/"0!6/@D)(=\)"24N#_D)(=X/[3GU`+D))2X,.>TY0!0!"0DD#@M`$" +M@`.0DD3@_W%M(I`%8."0DD_PD`5AX)"24/"0!6+@D))1\)`%8^"0DE+PPW3_ +MG_Z0DE#@TYY`'N`O\*/@M/\/Y/"CX+3_`^3P(I"24H`#D))1X`3P(I"24.`O +M\"*0DD'@9`%@`H%TD`!&X$0!_7]&$DU"D))3X'`RD)(YX&`5D))%$D-3D("% +M$BI_?X!^"!(OV8`&D`4B='_PD))`X/]Q;9"24W0!$DTX@$"0DE/@9`%P.)"2 +M1.#_<6WDD))3\)``1>!$`?U_11)-0I"2.>!@%9"2.Q)#4Y"`A1(J?W^`?@@2 +M+]F`!9`%(N3PD`6'X&2`\)"23^"0!83PD))0X)`%A?"0DE'@D`6&\)"24N"0 +M!8?P(M,0KP'#P-"0`DG4?&W4@`7L! +M>I)Y'+'"D)(\)"2J.!U\`20`=,20U_@D)(?\.]4?_][`7J2>1T27 +MJ1^0DH@20XNO(!4@[V`>D)*(Y'7P`1)#=!(IV?^0DH7D=?`!$D-T[Q)"38#; +MJQJJ&ZDB1]U(`)[`7H!>:"QPI"2CA)#:XL=BAZ)'Y"2BQ)#:Q(IV?_$ +M5`_U('L!>@%YHK'"D`&O=/_PD)*JX`3PD`'+X&2`\-#0DJ\B(N3U82*0`61T +MH/`BD))^X)"2#_`BD`#SX'\`,.,"?P$BD``"X%3@?P%@`G\`(I`&-'3_\.2C +M\*/PH_`BD`#SX##B#9`%0700\)`%6O"CY/`B=3`?=3$!Y/4RD`$XY3#PH^4Q +M\*/E,O`BD))^X+0!#)``\N`PYP5^_7\S(G[]?R\B=2@SY/4I=2H']2N0`3#E +M*/"CY2GPH^4J\*/E*_`BD)(0X%3P1`/P5`]$@/![`'H`>5:0DHX20XL+>I)Y +M$,$1TQ"O`931$=#0DJ\BTQ"O`I)Y9GT"\430T)*O +M(H^"CH.CHZ/D\"+D]5Y_8'X!@.W3$*\!P\#0D`0=X&`:D`4BX%208`>0`<;@ +M1$#PD`''X##AY'\`@`)_`=#0DJ\BTQ"O`_PD)*7X&`?HZ/@ +M_R0/]8+D-/SU@^!$@/!T$"_U@N0T_/6#X$2`\)"2F*/@__TD"/6"Y#3\]8/D +M\'0)+?6"Y#3\]8/@5/#P="$O]8+D-/SU@^!4]_"0DIC@_J/@_]#0DJ\B[V`+ +MD))^X+0!$.3_@`F0DG[@M`$%?P$2=^4B$E?0`33@52CU+*/@52GU+:/@52KU+J/@52OU +M+^4L(.`"0=^0`31T`?"%T4V%TDZ%TT^%U%"%U5&%UE*%UU.%V53E5%1`PQ/_ +MY5-4(&]P`D&515!__Y0@EX"3$]8+D-(WU +M@^2/\!)"@>535!__Y0@EX"2`]8+D-(?U@^2/\!)"@>4)TY0$0`-U"01U\`KE +M")"$`!)#7W7P`N4)$D-?X/ZCX/_E4U0?+__D/OYU\`KE")"$`!)#7W7P`N4) +M$D-?[O"C[_#E5"#F(^535!__Y0@EX"3$]8+D-(SU@^2/\!)"@>5/,.535!_U#:L) +MKPB1F^4D%"3]4`*`.I"29.!@*Y`!6^3PD`$\=`3P$DM5[V0!<"&0DH,22X>0 +M`5MT!?"0!I)T`?"0DF+P@`D22U6_`0,22WOE+##A(9`!-'0"\(715H725X73 +M6(7468756H766X777(7971)7(^4L,.,&D`$T=`CPY2PPY`F0`31T$/!#51#E +M+##E)I`!S^`PY1_@5-_PD`$T="#P=:@`=>@`$D]ED``#X%3[\!)/ZH#^Y2PP +MY@:0`31T0/#E+C#@%)"2?70!\)`!-O`21AZ199"2?>3PY2XPX0N0`39T`O!# +M54`1U^4N,.()D`$V=`3P$D::Y2XPXSB0`39T"/#E(60!<"SE)&`HD`%7Y/"0 +M`3QT`O"0DH/D\)"26^"0DH3PY/O]?U1^`1)+EY`!5W0%\.4N,.0KD`$V=!#P +MY2&T`2#E)&`4E5`=P`Q)+>^4N,.4?D`$V +M="#PY2&T`13E)&`0D))DX&0"8`422LR``Q))\^4N,.8;D`$V=$#PY2&T`1#E +M)&`,4R7^Y254!W`#$DM[Y2\PX0F0`3=T`O`225?0!]`&T`70!-`#T`+0`=`` +MT-#0@M"#T/#0X#*0DJ/@,.`NY2&T`2F0DJ+@!/#@M`H+D)*EX`3PY)"2HO"0 +MDJ7@_Y"2I.#3GU`'L9'DD)*E\"*/"HT+Y0M4'Y"2$O!T`2_U@N0TB/6#X)"2 +M$/"0!/W@D)(3M`$%=`/P@`-T`?"0DA/@_^O#GT`$KPJ`/)"2$.`E#?^C\*/@ +MD$'6D_[OTYY`%'0!)0KU@N0TB/6#Y/"M"Z\*`E>`D)(1X/]T`24*]8+D-(CU +M@^_P(JT'=?`)[9"*2!)#7^#_D)(5\'1G+?6"Y#21]8/@5!^0DA3PTY]`!J/@ +MD)(4\)"2%.#_)>`DGO6"Y#1!]8/DD_IT`9/[[R7@)&;U@N0T0?6#=`&3*__D +MDSK#$_[O$__M)>`DPO6"Y#2)]8/N\*/O\*\%D)(4X/T25X"0DA3@_R+D_P)) +MJ)"2)1)#BQ(IV50!_Y"2H^!4_D_PX##@`K&1D)(E$D-K$BG9PQ,PX`J0``$2 +M0B"0DJ3P(I"2(A)#B^\20Y1>$`%>!P)>-`->/05>1@9>DP=>3@E>5PQ>8`U> +M:0Y>>QQ>&2U>(BY>A#!>*SL``%Z,D)(B$D-K`G-CD)(B$D-K`G-ID)(B +M$D-K`G.>D)(B$D-K`G/FD)(B$D-K`G0?D)(B$D-K`G0XD)(B$D-K`G+,D)(B +M$D-K@$:0DB(20VL"=("0DB(20VL"3?^0DB(20VL"?D&0DB(20VL"?6F0DB(2 +M0VL"#_=?`)D(I&$D-?K8*L@Y"2+.SPH^WP[W7P":0D1/ET +MBC7P^GL!HQ)#BY"2)Q)#:Y```Q)"(%0/_Y"2+A)#:^\20DV0DB<20VN0``(2 +M0B#_D)(N$D-KD``![Q)"7Y"2)Q)#:Y```1)"(/^0DBS@_*/@_?6"C(/O\!(I +MV8V"C(.C\)"2*N#^D)(EX/\D@O6"Y#2)]8/N\)"2)N#^=?`)[Y"*2A)#7^[P +M=?`)[Y"*2Q)#7W0!\)"2*^#^=?`)[Y"*3!)#7^[PCP_O)>`DQO6"Y#2/KX+U +M$(\1Y0]U\`*D)`+Y=(DU\'42`?43B11U\`GE#Y"*1A)#7Z^"A8,5CQ;E#W7P +M":0D1/ETBC7P=14/)>`DQO6" +MY#2/]8-T#_"C=/6`)^4/)>`DQO6"Y#2/]8-T#_"C=/"`$N4/)>`DQO6"Y#2/ +M]8/D\*-T#?#E#R7@)`+U@N0TB?6#Y/"C\"&5D`1'X*L2JA.I%!)"39`$1N"K +M$JH3J120``$20E^0!$7@A1&"A1"#\)`$1"&,D`1+X*L2JA.I%!)"39`$2N"K +M$JH3J120``$20E^0!$G@A1&"A1"#\)`$2(!8D`1/X*L2JA.I%!)"39`$3N"K +M$JH3J120``$20E^0!$W@A1&"A1"#\)`$3(`KD`13X*L2JA.I%!)"39`$4N"K +M$JH3J120``$20E^0!%'@A1&"A1"#\)`$4."%$8*%$(.C\*L2JA.I%,`#P`+` +M`1(IV?^K%ZH8J1D2*=E?T`'0`M`#$D)-JQ+E%"0!^>0U$_K``\`"P`$2*=G_ +MJQ>J&*D9D``!$D(@7]`!T`+0`Q)"3841@H40@\"#P(+@_X46@H45@^#^[U[0 +M@M"#\(41@H40@Z/`@\""X/^%%H*%%8.CX/[O7M""T(/PY0\EX"0"]8+D-(GU +M@^#^H^!.8$N0DC%T"_"0DC'@_\.4`%`"0=IT`7X`J`<(@`7#,\XSSMCY_^4/ +M)>`D`O6"Y#2)]8/@7OZCX%].8`J0DC'@)!"C\(!HD)(QX!3P@+OE#R7@),;U +M@N0TC_6#X/ZCX$Y@1Y"2,70/\)"2,>#_PY0`0#QT`7X`J`<(@`7#,\XSSMCY +M_^4/)>`DQO6"Y#2/]8/@7OZCX%].8`B0DC'@H_"`#9"2,>`4\("_Y)"2,O#E +M#R7@),;U@N0TC_6#X/ZCX$Y@1N20DC'PD)(QX/_#E!!``F&3=`%^`*@'"(`% +MPS/.,\[8^?_E#R7@),;U@N0TC_6#X%[^H^!?3F`&D)(QX(!CD)(QX`3P@+_E +M#R7@)`+U@N0TB?6#X/ZCX$Y@1N20DC'PD)(QX/_#E`Q0/'0!?@"H!PB`!<,S +MSC/.V/G_Y0\EX"0"]8+D-(GU@^!>_J/@7TY@")"2,>`D$(`)D)(QX`3P@+_D +MD)(S\)"2,N#_=?`)Y0^0BD@20U_O\)"2,^#^=?`)Y0^0BDD20U_N\.4/PY0@ +M4"QTA"4/]8+D-`3U@^#3GT`"@$=TA"4/]8+D-`3U@^##GE`(D)(SX*/P@#>` +M+72F)0_U@N0TD/6#X/^0DC+@_N_3GD`'D)(T[O"`%Y"2,^#^[\.>4`6C[O"` +M")"2,N"0DC3PD)(TX/VO#Q)7@)"2-.#_="8E#_6"Y#21]8/O\)"2,N#_TY03 +M0`>0BD-T`_`B[].4"T`'D(I#=`+P(N_3E`-`!Y"*0W0!\"+DD(I#\"+3$*\! +MP\#0Y/W\[S#@`GV`[\,3D/T0\*X$KP70T)*O(L#@P/#`@\""P-!UT`#``,`! +MP`+``\`$P`7`!L`'4Y'OD`!1X/^0`%7@7_4]D`!2X/^0`%;@7_4^Y3TPY`:0 +M`%5T$/#E/3#E!I``570@\.4],.8;D`!5=$#PD))"X%0#_[\#"Y"2/^!@!7\! +M$DU7Y3TPYQ60`%5T@/"0DD+@5`/_OP,%?P(235?E/C#@!I``5G0!\.4^,.$& +MD`!6=`+PY3XPX@:0`%9T!/#E/C#C!I``5G0(\-`'T`;0!=`$T`/0`M`!T`#0 +MT-""T(/0\-#@,N_#E"!0.>\PX!?MQ%3P_>_#$_XDI/6"Y#0$]8/@5`^`$._# +M$_XDI/6"Y#0$]8/@5/#P=*0N]8+D-`3U@^!-\"*M!^W#E"!0$W2$+?6"Y#0$ +M]8/@5'^0DBGP@!%TIBWU@N0TD/6#X%1_D)(I\)"2*>#Y5!^C\'7P">V0BD@2 +M0U_@_Y"2+/#M)>`D`O6"Y#2)]8/@^Z/@D)(MR_"CZ_#M)>`DQO6"Y#2/]8/@ +M^Z/@D)(OR_"CZ_"0DBK@_B7@)&;U@N0T0?6#Y)/Z=`&3^^TEX"3"]8+D-(GU +M@^KPH^OP[L.?0`+!^)"2*N#_=&XD\/]T`7X`J`<(@`7#,\XSSMCY_Y"2+>!>_J/@7TYP +M)Y"2*^#_PY004%ET`7X`J`<(@`7#,\XSSMCY_Y"2+^!>_J/@7TY@/)"2*^"T +M$0V0DB[@,.<&D)(K=!?PD)(KX/]D$V`$[[02#9"2+>`PX`:0DBMT&/"0DBO@ +MD)(J\)"2*?"`0I"2*^`$\,%6D)(LX/R0DBK@_VQP<71G+?6"Y#21]8/O\'7P +M">V0BDH20U_@M`$0Z2#F#)"2*N!$0)"2*?"``Z\!(I"2*N#_)>`DGO6"Y#1! +M]8/DD_IT`9/[[R7@)&;U@N0T0?6#=`&3*__DDSK#$_[O$__M)>`DPO6"Y#2) +M]8/N\*/O\(!FD)(JX-.<0%Z0DBS@_W1G+?6"Y#21]8/O\)"2*N_PD)(I\/RC +MX/\EX"2>]8+D-$'U@^23^G0!D_OO)>`D9O6"Y#1!]8-T`9,K_^23.L,3_N\3 +M_^TEX"3"]8+D-(GU@^[PH^_PKP0B=`$M]8+D-(CU@^3PKP60DBG@1(#]$E>` +MD)(IX$2`_R*L!^S#E"!0$W2$+/6"Y#0$]8/@5'^0DBGP@!%TIBSU@N0TD/6# +MX%1_D)(I\)"2*>!4'_^0DBSP=?`)[)"*21)#7^"0DB[P=?`)[)"*2!)#7^#^ +MD)(O\.PEX"3&]8+D-(_U@^#[H^"0DC#+\*/K\.PEX"0"]8+D-(GU@^#[H^"0 +MDC++\*/K\._3GD`,D)(OX)"2+/"0DBGP[7`"(6J0DBWM\)"2*>`PY@Z0DBS@ +MD)(I\)"2+>`4\)"2+>!P`B%JD)(LX/_3E`!0`B%JY)"2*_#O%)"2*O"0DB[@ +M_9"2*N#_TYU`;^^4$$`A[R3P_W0!?@"H!PB`!<,SSC/.V/G_D)(RX%[^H^!? +M3G`GD)(JX/_#E!!0-W0!?@"H!PB`!<,SSC/.V/G_D)(PX%[^H^!?3F`:D)(J +MX)"2*?"0DBO@!/"0DBW@_Y"2*^!O8`B0DBK@%/"`@Y"2+>#_D)(KX,.?4`^0 +MDBK@M04(D)(NX)"2*?"0DBG@_R7@))[U@N0T0?6#Y)/Z=`&3^^\EX"1F]8+D +M-$'U@W0!DRO_Y),ZPQ/^[Q/_["7@),+U@N0TB?6#[O"C[_"O!)"2*>#]$E>` +MD)(IX/\BY)"2&O"0DAK@_\.40$`#`G#5[[0@%)".Q>`$\)")`>#_D([%X+4' +M`N3PD)(:X/]U\`F0BDL20U_@9`%@`P)PS.\EX"2`]8+D-(?U@^#^H^#3E`#N +ME`!0`P)PS)"2&N"4($`)D([%X&`#`G#5D)(:X'7P"J0D`/ETA#7P=1(!]1.) +M%.#_)>`D@/6"Y#2']8/@_:/@D)(?S?"C[?#O)>`DQ/6"Y#2,]8/@_Z/@D)(A +MS_"C[_"0DAK@_\.4(%`3=(0O]8+D-`3U@^!4/Y"2&_"`%)"2&N`DIO6"Y#20 +M]8/@5#^0DAOPD)(;X/Y4'Z/PD)(:X/]U\`F0BD@20U_@D)(D\'3F+_6"Y#20 +M]8/@PY0%0`*A'I"2).#_D)(0`;OD$$2@`>0DAS@D$$N +MDY"2(_"0DB/@=?`&I"10^71`-?!U#__U$(D1D)(;X)!!\I/_TY"2(N"?D)(A +MX)0`0`V0DAK@_^3]$F?\`G!BD)(:X"7@),+U@N0TB?6#X/^CX)"2'<_PH^_P +MJP^J$*D1$BG9_WX`JQ*J$ZD4$D*7_:SP$BGRD)(=[H_P$D*!JP^J$*D1D``! +M$D(@_WX`JQ*J$ZD4D``"$D+"_:SP$BGRD)(=[H_P$D*!JP^J$*D1D``"$D(@ +M_WX`JQ*J$ZD4D``$$D+"_:SP$BGRD)(=[H_P$D*!JP^J$*D1D``#$D(@_WX` +MJQ*J$ZD4D``&$D+"_:SP$BGRD)(=[H_P$D*!JP^J$*D1D``$$D(@_WX`JQ*J +M$ZD4D``($D+"_:SP$BGRD)(=[H_P$D*!JP^J$*D1D``%$D(@_WX`D)(?X/RC +MX/T2*?+3D)(>X)^0DAW@GD`,H^"?\)"2'>">\(`'Y)"2'?"C\)"2'>#\H^#] +MD)(:X/\EX"3"]8+D-(GU@^SPH^WPD)(;X"7@)&;U@N0T0?6#Y)/Z=`&3^]/M +MF^R:0`8299L"<#"0DAO@)>`DGO6"Y#1!]8/DD_YT`9/_PY"2'N"?D)(=X)Y` +M`P)P,)"2&N#_?0$29_P"<#"0DAK@_R3F]8+D-)#U@^#\9`5@`L']D(I#X/ZT +M`PN0DAS@PY090#V`+NZT`@N0DAS@PY010"Z`'Y"*0^#^M`$+D)(20CH3PD)(:X/XDA/6"Y#2,]8/@D)(H +M\'1$+O6"Y#2.]8/@_L.4,%`*Y)"2*/!TYB_!IY".A.!D`6`"P9R0DAK@)(7U +M@N0TCO6#X&0*8%N0DAK@_^XD!?OD,_IT02_U@N0TB/6#X/_3F^ID@/AT@)A0 +M.)"2&N#^[R0%^^0S^G1$+O6"Y#2.]8/@TYOJ9(#X=("84!:0DAK@)";U@N0T +MD?6#X/^0DAS@;V!6D)(:X"1$]8+D-([U@^#_TY1"0`B0DBAT!?"`$>_3E#F0 +MDBA`!70#\(`#=`'PD)(:X/\D1/6"Y#2.]8/@_G1!+_6"Y#2(]8/N\)"2&N`D +MA?6"Y#2.@"^0DAK@_R3F]8+D-)#U@^3P=(4O]8+D-([U@^`$\(`4Y)"2*/"0 +MDAK@).;U@N0TD/6#Y/"0DAS@_I"2&N#_)";U@N0TD?6#[O"0DBC@_G2$+_6" +MY#2,]8/N\'7P">^0BDP20U_@M`$1Y)"2*/!TYB_U@N0TD/6#Y/"0DBC@_0)P +M+>QD!F`#`G`PD)(=\*/PD$(3D_]^`)"2'^#\H^#]$BGRD)(F[O"C[_"0DAK@ +M)(3U@N0TC/6#X)"2*/#DD)(E\)"2)>#_TY0$4$>K$JH3J11U\`+OI/6"A?"# +M$D+"_:SP[Y!"#I/_?@`2*?*0DAWNC_`20H&0DB;@_J/@_].0DA[@GY"2'>"> +M4`B0DB7@!/"`KY"2)>##$_"0DBC@_[0!#9"2)>!P79"2*`3P@%OOM`,=D)(E +MX/]P")"2*'0#\(!([[0!")"2*'0!\(`\@#60DBC@9`5P,I"2)>#_<`B0DBAT +M!?"`#^^0DBBT`05T`_"``W0!\-.0DB+@E`.0DB'@E`!`!>20DBCPTY"2(N"4 +M`Y"2(>"4`$`%Y)"2*/"0DBC@_9"2&N#_)(3U@N0TC/6#[?`295N0DAK@_R3F +M]8+D-)#U@^#3E`50#W3F+_6"Y#20]8/@!/"`#Y"2&N`DYO6"Y#20]8/D\*L2 +MJA.I%.3U\!)"^JL2JA.I%)```N3U\!)#&9``!.3U\!)#&9``!N3U\!)#&9`` +M".3U\!)#&9"2&N#_)>`D@/6"Y#2']8/D\*/P[R7@),3U@N0TC/6#Y/"C\.\E +MX"1$]8+D-(WU@^3PH_"0DAK@!/`":<$BD`1$=!'PHW3P\*-T#_"CY/"0DAKP +MD)(:X/_#E!!0%'2D+_6"Y#0$]8/D\)"2&N`$\(#BY)".Q?"0B0$$\.20DAKP +MD)(:X/_#E$!``D'+=?`*[Y"$`!)#7^3PH_!U\`KOD(0"$D-?Y/"C\'7P"N^0 +MA`020U_D\*/P=?`*[Y"$!A)#7^3PH_!U\`KOD(0($D-?Y/"C\'0F+_6"Y#21 +M]8-T$_!TA2_U@N0TCO6#Y/!TA"_U@N0TC/6#Y/#O)>`D@/6"Y#2']8/D\*/P +M[R7@),3U@N0TC/6#Y/"C\.\EX"3$]8+D-(WU@^3PH_#O)>`D1/6"Y#2-]8/D +M\*/P[R7@),;U@N0TCO6#Y/"C\.\EX"1&]8+D-(_U@^3PH_!TAB_U@N0TD/6# +MY/!T1B_U@N0TD/6#Y/!TYB_U@N0TD/6#Y/"00<23_G0!D_^008QT`9,O_^23 +M/L,3_N\3_Y"2&N#])>`DPO6"Y#2)]8/N\*/O\'7P">V0BDL20U]T`?!U\`GM +MD(I*$D-?=`'P=((M]8+D-(GU@W0,\'7P">V0BD820U]T__"C\'7P">V0BD02 +M0U_D\*-T#_!U\`GMD(I($D-?=!/P=?`)[9"*21)#7^3P[<.4(%`/=(0M]8+D +M-`3U@W03\(`-=*8M]8+D-)#U@W03\)"2&N`$\"$8(A(IV?_#E$!0%)```A)" +M(/YT1"_U@N0TCO6#[O`B[[1`"I```A)"()"*0O`BD``$$D(@_U0__N]4@,03 +M$Q-4`?VO!@)>J!(IV9")`?`BD)*:[O"C[_#DH_"C\)"2FN#^H^#U@HZ#X&`L +MPY"2G>"4Z)"2G."4`T`*D`'&X$00\'\`(I"2G.1U\`$20H%_"GX`$C=4@,9_ +M`2(2*=GU(2+3$*\!P\#0D)(E$BJ+`````)```1)"()"29/"0``,20B"0DE3P +M$D?3PH^_P@`^0DE]T!?"0DEWD\*-T`_"0 +MDEW@H^"0!5CP(A(IV9"27/!@!^3]?P0210V0DES@D`'G\"*0`@G@_1(IV?ZO +M!>TND))V\)```1)"(/_M+Y"2=_"0``(20B#_[2^0DGCPD``#$D(@_^TOD))Y +M\)``!!)"(/^N!>TOD))Z\"+3$*\!P\#0D)(E$D.+D)(E$D-KD``!$D+"^N7P +M)`#_Y#K^D)(E$D-KD``![H_P$D,9$BG9_V`LM5X6D)(E$D-KD``!$D+"96!P +M!.5?9?!@(Y"2)1)#:Y```1)"PO^N\)'X@!"0DB420VL2*=EE7F`#$E?3T-"2 +MKR*0DBCN\*/O\'5>`8Y?]6#D_7\+L3SD_7\"L3P23[OD_Q)/K^3U8I`!R>5B +M\)"2*.#\H^#][/N-1.3U17T!?V!^`0(UJ],0KP'#P-"0DBOM\)"2*N_PTY0' +M4$^CX'`:D)(JX/]T`:@'"(`"PS/8_/3_D`!'X%_P@!>0DBK@_W0!J`<(@`+# +M,]C\_Y``1^!/\!)/ZI"2*N#_=`&H!PB``L,SV/ST_Y``1H!:D)(JX"3X\*/@ +M`$\(#KD)(:X,.49%`0D``PX*L1JA*I$Q)" +M37\!(G\`(N20DJ;PH_"0!?C@<`^CX'`+H^!P!Z/@<`-_`2+3D)*GX)3HD)*F +MX)0#0`-_`")_,GX`$C=4D)*FY'7P`1)"@8#&D)'_$D-3D("%$BI_?WA^"!(O +MV9"2`Q)#4Y"`A1(J?W\$?@P2+]F0D@<20U.0@(42*G]_`'X($B_9D)(+$D-3 +MD("%$BI_?W!^#A(OV9"`61(JBP`#+97D_?\2-(&0DG[@M`$1D(!9$BJ+``,M +ME>3]?P$2-($B?WA^"!(GWI"1_Q(J?W\$?@P2)]Z0D@,2*G]_`'X($B?>D)(' +M$BI_D))^X)"1_[0!#1)#4^]4Q__M5,?]@`<20U/O5,?_[)"`A1(J?W]X?@@2 +M+]F0D@,20U/O5`__[)"`A1(J?W\$?@P2+]F0D@<20U/O1`+_[)"`A1(J?W\` +M?@@2+]E_<'X.$B?>D)(+$BI_D("%$BJ+`!LEH']P?@X2+]F0@%D2*HL````` +MY/W_$C2!D))^X+0!$9"`61(JBP````#D_7\!$C2!(N]P`P)YG9"2#^!@`P)] +M:)"1^Q)#4Y"`A1(J?W^,?@@2+]F0D:<20U.0@(42*G]_1'X($B_9D)&K$D-3 +MD("%$BI_?UQ^"!(OV9"1KQ)#4Y"`A1(J?W]L?@X2+]F0D;,20U.0@(42*G]_ +M<'X.$B_9D)&W$D-3D("%$BI_?W1^#A(OV9"1NQ)#4Y"`A1(J?W]X?@X2+]F0 +MD;\20U.0@(42*G]_?'X.$B_9D)'#$D-3D("%$BI_?X!^#A(OV9"1QQ)#4Y"` +MA1(J?W^$?@X2+]F0D,20U.0@(42 +M*G]_X'X.$B_9D)'G$D-3D("%$BI_?^Q^#A(OV9"1ZQ)#4Y"`A1(J?W\$?@P2 +M+]F0D>\20U.0@(42*G]_!'X-$B_9D)'S$D-3D("%$BI_?PQ^"1(OV9"1]Q)# +M4Y"`A1(J?W\$?@@2+]F0D@]T`?`BD)(/X&0!8`*A:'^,?@@2)]Z0D?L2*G]_ +M1'X($B?>D)&G$BI_?UQ^"!(GWI"1JQ(J?W]L?@X2)]Z0D:\2*G]_<'X.$B?> +MD)&S$BI_?W1^#A(GWI"1MQ(J?W]X?@X2)]Z0D;L2*G]_?'X.$B?>D)&_$BI_ +M?X!^#A(GWI"1PQ(J?W^$?@X2)]Z0D<<2*G]_B'X.$B?>D)'+$BI_?XQ^#A(G +MWI"1SQ(J?W_0?@X2)]Z0D=,2*G]_U'X.$B?>D)'7$BI_?]A^#A(GWI"1VQ(J +M?W_D)'C$BI_?^Q^#A(GWI"1YQ(J?W\$?@P2 +M)]Z0D>L2*G]_!'X-$B?>D)'O$BI_?PQ^"1(GWI"1\Q(J?W\$?@@2)]Z0D?<2 +M*G]_C'X($B?>D)*>$BI_D)*>$D-3[43`_>R0DIX2*G^0DIX20U.0@(42*G]_ +MC'X($B_9D("%$BJ+``$``']$?@@2+]F0@(42*HL`VR6D?UQ^"!(OV9"`A1(J +MBR#;):1_;'X.$B_9D("%$BJ+(-LEI']P?@X2+]F0@(42*HL$&R6D?W1^#A(O +MV9"`A1(JBP0;):1_>'X.$B_9D("%$BJ+!!LEI']\?@X2+]F0@(42*HL$&R6D +M?X!^#A(OV9"`A1(JBV/;):1_A'X.$B_9D("%$BJ+!!LEI'^(?@X2+]F0@(42 +M*HL@VR6D?XQ^#A(OV9"`A1(JBR#;):1_T'X.$B_9D("%$BJ+(-LEI'_4?@X2 +M+]F0@(42*HL@VR6D?]A^#A(OV9"`A1(JBP`;):1_W'X.$B_9D("%$BJ+`!LE +MI'_@?@X2+]F0@(42*HLDVR6D?^Q^#A(OV7\$?@P2)]Z0DIX2*G^0DIX20U/D +M_^R0DIX2*G^0DIX20U/O1!'_[)"2GA(J?Y"2GA)#4Y"`A1(J?W\$?@P2+]E_ +M!'X-$B?>D)*>$BI_D)*>$D-3[U3P_^R0DIX2*G^0DIX20U/O1`'_[)"2GA(J +M?Y"2GA)#4Y"`A1(J?W\$?@T2+]E_#'X)$B?>D)*>$BI_D)*>$D-3Y/_LD)*> +M$BI_D)*>$D-3[T01_^R0DIX2*G^0DIX20U.0@(42*G]_#'X)$B_9?PQ^"1(G +MWI"2GA(J?Y"2GA)#4^U4#_WL5/#\D)*>$BI_D)*>$D-3[400_>Q$`?R0DIX2 +M*G^0DIX20U.0@(42*G]_#'X)$B_9?P1^"!(GWI"2GA(J?Y"2GA)#4^]4\/_L +MD)*>$BI_D)*>$D-3[T0!_^R0DIX2*G^0DIX20U.0@(42*G]_!'X($B_9Y)"2 +M#_`BBP^*$(D1D``"$D(@D))"\.`PX$N0DCET`?!_@'X($B?>D)([$BI_JP^J +M$*D1D``!$D(@_^3\_?YX&A(J;*@$J06J!JL'D)([$D-3[%0#_!)#1I"211(J +M?Y`%(N3P@"WDD)(Y\'^`?@@2)][L5`/\[$3`_)"2.Q(J?Y"2.Q)#4Y"`A1(J +M?W^`?@@2+]F0DD+@,.$;?0Q_1Q)-0I``2.!$#/U_2!)-0I``1N!$$(`>D`!' +MX%3S_7]'$DU"D`!(X%3S_7]($DU"D`!&X%3O_7]&$DU"Y)"2/_`BD``"$D(@ +MD))#\.!@!.#T<"&BK^0S]0_"KY``1^!4^_U_1Q)-0GU`?P$2-J_E#R3_DJ\B +MY/U_11)-0I`$_>3PH_"0DD/PD)))\)"23/"0DDKPD))-\)"22_"0DD[PD)(U +M!/#DH_"C\*/PD)(Z\)"2/_"0DD'PD))3\)"21/"0DD#PD)(Y\)``4>!$P/U_ +M40)-0I"24^!D`6`(D))!X&`"X:F0DC7@PY3_4`7@!/"`.Y"2-N##E/]0!N`$ +M\.2`*)"2-^##E/]0"N`$\.20DC;P@!60DCC@PY3_4!#@!/#DD)(W\)"2-O"0 +MDC7PD`!$X%0,8';@,.(RD)))X,.4_U`%X`3P@"20DDK@PY3_4`;@!/#D@!&0 +MDDO@PY3_4`S@!/#DD))*\)"22?"0`$3@,.,RD)),X,.4_U`%X`3P@"20DDW@ +JPY3_4`;@!/#D@!&0DD[@PY3_4`S@!/#DD))-\)"23/"0!/W@1`'P(GP. +` +end Index: projects/clang380-import/sys/crypto/sha1.h =================================================================== --- projects/clang380-import/sys/crypto/sha1.h (revision 293015) +++ projects/clang380-import/sys/crypto/sha1.h (revision 293016) @@ -1,72 +1,72 @@ /* $FreeBSD$ */ /* $KAME: sha1.h,v 1.5 2000/03/27 04:36:23 sumikawa Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. */ /* * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) * based on: http://csrc.nist.gov/fips/fip180-1.txt * implemented by Jun-ichiro itojun Itoh */ #ifndef _NETINET6_SHA1_H_ #define _NETINET6_SHA1_H_ struct sha1_ctxt { union { u_int8_t b8[20]; u_int32_t b32[5]; } h; union { u_int8_t b8[8]; u_int64_t b64[1]; } c; union { u_int8_t b8[64]; u_int32_t b32[16]; } m; u_int8_t count; }; +typedef struct sha1_ctxt SHA1_CTX; #ifdef _KERNEL extern void sha1_init(struct sha1_ctxt *); extern void sha1_pad(struct sha1_ctxt *); extern void sha1_loop(struct sha1_ctxt *, const u_int8_t *, size_t); extern void sha1_result(struct sha1_ctxt *, caddr_t); /* compatibilty with other SHA1 source codes */ -typedef struct sha1_ctxt SHA1_CTX; #define SHA1Init(x) sha1_init((x)) #define SHA1Update(x, y, z) sha1_loop((x), (y), (z)) #define SHA1Final(x, y) sha1_result((y), (x)) #endif /* _KERNEL */ #define SHA1_RESULTLEN (160/8) #endif /*_NETINET6_SHA1_H_*/ Index: projects/clang380-import/sys/dev/cxgb/ulp/tom/cxgb_l2t.c =================================================================== --- projects/clang380-import/sys/dev/cxgb/ulp/tom/cxgb_l2t.c (revision 293015) +++ projects/clang380-import/sys/dev/cxgb/ulp/tom/cxgb_l2t.c (revision 293016) @@ -1,460 +1,460 @@ /*- * Copyright (c) 2012 Chelsio Communications, 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. * 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$"); #include "opt_inet.h" #ifdef TCP_OFFLOAD #include #include #include #include #include #include #include #include #include #include #include #include "cxgb_include.h" #include "ulp/tom/cxgb_tom.h" #include "ulp/tom/cxgb_l2t.h" #define VLAN_NONE 0xfff #define SA(x) ((struct sockaddr *)(x)) #define SIN(x) ((struct sockaddr_in *)(x)) #define SINADDR(x) (SIN(x)->sin_addr.s_addr) /* * Module locking notes: There is a RW lock protecting the L2 table as a * whole plus a mutex per L2T entry. Entry lookups and allocations happen * under the protection of the table lock, individual entry changes happen * while holding that entry's mutex. The table lock nests outside the * entry locks. Allocations of new entries take the table lock as writers so * no other lookups can happen while allocating new entries. Entry updates * take the table lock as readers so multiple entries can be updated in * parallel. An L2T entry can be dropped by decrementing its reference count * and therefore can happen in parallel with entry allocation but no entry * can change state or increment its ref count during allocation as both of * these perform lookups. * * When acquiring multiple locks, the order is llentry -> L2 table -> L2 entry. */ static inline unsigned int arp_hash(u32 key, int ifindex, const struct l2t_data *d) { return jhash_2words(key, ifindex, 0) & (d->nentries - 1); } /* * Set up an L2T entry and send any packets waiting in the arp queue. Must be * called with the entry locked. */ static int setup_l2e_send_pending(struct adapter *sc, struct l2t_entry *e) { struct mbuf *m; struct cpl_l2t_write_req *req; struct port_info *pi = &sc->port[e->smt_idx]; /* smt_idx is port_id */ mtx_assert(&e->lock, MA_OWNED); m = M_GETHDR_OFLD(pi->first_qset, CPL_PRIORITY_CONTROL, req); if (m == NULL) { log(LOG_ERR, "%s: no mbuf, can't setup L2 entry at index %d\n", __func__, e->idx); return (ENOMEM); } req->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx)); req->params = htonl(V_L2T_W_IDX(e->idx) | V_L2T_W_IFF(e->smt_idx) | V_L2T_W_VLAN(e->vlan & EVL_VLID_MASK) | V_L2T_W_PRIO(EVL_PRIOFTAG(e->vlan))); memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); t3_offload_tx(sc, m); /* * XXX: We used pi->first_qset to send the L2T_WRITE_REQ. If any mbuf * on the arpq is going out via another queue set associated with the * port then it has a bad race with the L2T_WRITE_REQ. Ideally we * should wait till the reply to the write before draining the arpq. */ while (e->arpq_head) { m = e->arpq_head; e->arpq_head = m->m_next; m->m_next = NULL; t3_offload_tx(sc, m); } e->arpq_tail = NULL; return (0); } /* * Add a packet to the an L2T entry's queue of packets awaiting resolution. * Must be called with the entry's lock held. */ static inline void arpq_enqueue(struct l2t_entry *e, struct mbuf *m) { mtx_assert(&e->lock, MA_OWNED); m->m_next = NULL; if (e->arpq_head) e->arpq_tail->m_next = m; else e->arpq_head = m; e->arpq_tail = m; } static void resolution_failed_mbuf(struct mbuf *m) { log(LOG_ERR, "%s: leaked mbuf %p, CPL at %p", __func__, m, mtod(m, void *)); } static void resolution_failed(struct l2t_entry *e) { struct mbuf *m; mtx_assert(&e->lock, MA_OWNED); while (e->arpq_head) { m = e->arpq_head; e->arpq_head = m->m_next; m->m_next = NULL; resolution_failed_mbuf(m); } e->arpq_tail = NULL; } static void update_entry(struct adapter *sc, struct l2t_entry *e, uint8_t *lladdr, uint16_t vtag) { mtx_assert(&e->lock, MA_OWNED); /* * The entry may be in active use (e->refcount > 0) or not. We update * it even when it's not as this simplifies the case where we decide to * reuse the entry later. */ if (lladdr == NULL && (e->state == L2T_STATE_RESOLVING || e->state == L2T_STATE_FAILED)) { /* * Never got a valid L2 address for this one. Just mark it as * failed instead of removing it from the hash (for which we'd * need to wlock the table). */ e->state = L2T_STATE_FAILED; resolution_failed(e); return; } else if (lladdr == NULL) { /* Valid or already-stale entry was deleted (or expired) */ KASSERT(e->state == L2T_STATE_VALID || e->state == L2T_STATE_STALE, ("%s: lladdr NULL, state %d", __func__, e->state)); e->state = L2T_STATE_STALE; } else { if (e->state == L2T_STATE_RESOLVING || e->state == L2T_STATE_FAILED || memcmp(e->dmac, lladdr, ETHER_ADDR_LEN)) { /* unresolved -> resolved; or dmac changed */ memcpy(e->dmac, lladdr, ETHER_ADDR_LEN); e->vlan = vtag; setup_l2e_send_pending(sc, e); } e->state = L2T_STATE_VALID; } } static int resolve_entry(struct adapter *sc, struct l2t_entry *e) { struct tom_data *td = sc->tom_softc; struct toedev *tod = &td->tod; struct sockaddr_in sin = {0}; - uint8_t dmac[ETHER_ADDR_LEN]; + uint8_t dmac[ETHER_HDR_LEN]; uint16_t vtag = EVL_VLID_MASK; int rc; sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); SINADDR(&sin) = e->addr; rc = toe_l2_resolve(tod, e->ifp, SA(&sin), dmac, &vtag); if (rc == EWOULDBLOCK) return (rc); mtx_lock(&e->lock); update_entry(sc, e, rc == 0 ? dmac : NULL, vtag); mtx_unlock(&e->lock); return (rc); } int t3_l2t_send_slow(struct adapter *sc, struct mbuf *m, struct l2t_entry *e) { again: switch (e->state) { case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ if (resolve_entry(sc, e) != EWOULDBLOCK) goto again; /* entry updated, re-examine state */ /* Fall through */ case L2T_STATE_VALID: /* fast-path, send the packet on */ return (t3_offload_tx(sc, m)); case L2T_STATE_RESOLVING: mtx_lock(&e->lock); if (e->state != L2T_STATE_RESOLVING) { mtx_unlock(&e->lock); goto again; } arpq_enqueue(e, m); mtx_unlock(&e->lock); if (resolve_entry(sc, e) == EWOULDBLOCK) break; mtx_lock(&e->lock); if (e->state == L2T_STATE_VALID && e->arpq_head) setup_l2e_send_pending(sc, e); if (e->state == L2T_STATE_FAILED) resolution_failed(e); mtx_unlock(&e->lock); break; case L2T_STATE_FAILED: resolution_failed_mbuf(m); return (EHOSTUNREACH); } return (0); } /* * Allocate a free L2T entry. Must be called with l2t_data.lock held. */ static struct l2t_entry * alloc_l2e(struct l2t_data *d) { struct l2t_entry *end, *e, **p; rw_assert(&d->lock, RA_WLOCKED); if (!atomic_load_acq_int(&d->nfree)) return (NULL); /* there's definitely a free entry */ for (e = d->rover, end = &d->l2tab[d->nentries]; e != end; ++e) { if (atomic_load_acq_int(&e->refcnt) == 0) goto found; } for (e = &d->l2tab[1]; atomic_load_acq_int(&e->refcnt); ++e) continue; found: d->rover = e + 1; atomic_add_int(&d->nfree, -1); /* * The entry we found may be an inactive entry that is * presently in the hash table. We need to remove it. */ if (e->state != L2T_STATE_UNUSED) { int hash = arp_hash(e->addr, e->ifp->if_index, d); for (p = &d->l2tab[hash].first; *p; p = &(*p)->next) { if (*p == e) { *p = e->next; break; } } e->state = L2T_STATE_UNUSED; } return (e); } struct l2t_entry * t3_l2t_get(struct port_info *pi, struct ifnet *ifp, struct sockaddr *sa) { struct tom_data *td = pi->adapter->tom_softc; struct l2t_entry *e; struct l2t_data *d = td->l2t; uint32_t addr = SINADDR(sa); int hash = arp_hash(addr, ifp->if_index, d); unsigned int smt_idx = pi->port_id; rw_wlock(&d->lock); for (e = d->l2tab[hash].first; e; e = e->next) { if (e->addr == addr && e->ifp == ifp && e->smt_idx == smt_idx) { l2t_hold(d, e); goto done; } } /* Need to allocate a new entry */ e = alloc_l2e(d); if (e) { mtx_lock(&e->lock); /* avoid race with t3_l2t_free */ e->next = d->l2tab[hash].first; d->l2tab[hash].first = e; e->state = L2T_STATE_RESOLVING; e->addr = addr; e->ifp = ifp; e->smt_idx = smt_idx; atomic_store_rel_int(&e->refcnt, 1); KASSERT(ifp->if_vlantrunk == NULL, ("TOE+VLAN unimplemented.")); e->vlan = VLAN_NONE; mtx_unlock(&e->lock); } done: rw_wunlock(&d->lock); return (e); } void t3_l2_update(struct toedev *tod, struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr, uint16_t vtag) { struct tom_data *td = t3_tomdata(tod); struct adapter *sc = tod->tod_softc; struct l2t_entry *e; struct l2t_data *d = td->l2t; u32 addr = *(u32 *) &SIN(sa)->sin_addr; int hash = arp_hash(addr, ifp->if_index, d); rw_rlock(&d->lock); for (e = d->l2tab[hash].first; e; e = e->next) if (e->addr == addr && e->ifp == ifp) { mtx_lock(&e->lock); goto found; } rw_runlock(&d->lock); /* * This is of no interest to us. We've never had an offloaded * connection to this destination, and we aren't attempting one right * now. */ return; found: rw_runlock(&d->lock); KASSERT(e->state != L2T_STATE_UNUSED, ("%s: unused entry in the hash.", __func__)); update_entry(sc, e, lladdr, vtag); mtx_unlock(&e->lock); } struct l2t_data * t3_init_l2t(unsigned int l2t_capacity) { struct l2t_data *d; int i, size = sizeof(*d) + l2t_capacity * sizeof(struct l2t_entry); d = malloc(size, M_CXGB, M_NOWAIT | M_ZERO); if (!d) return (NULL); d->nentries = l2t_capacity; d->rover = &d->l2tab[1]; /* entry 0 is not used */ atomic_store_rel_int(&d->nfree, l2t_capacity - 1); rw_init(&d->lock, "L2T"); for (i = 0; i < l2t_capacity; ++i) { d->l2tab[i].idx = i; d->l2tab[i].state = L2T_STATE_UNUSED; mtx_init(&d->l2tab[i].lock, "L2T_E", NULL, MTX_DEF); atomic_store_rel_int(&d->l2tab[i].refcnt, 0); } return (d); } void t3_free_l2t(struct l2t_data *d) { int i; rw_destroy(&d->lock); for (i = 0; i < d->nentries; ++i) mtx_destroy(&d->l2tab[i].lock); free(d, M_CXGB); } static int do_l2t_write_rpl(struct sge_qset *qs, struct rsp_desc *r, struct mbuf *m) { struct cpl_l2t_write_rpl *rpl = mtod(m, void *); if (rpl->status != CPL_ERR_NONE) log(LOG_ERR, "Unexpected L2T_WRITE_RPL status %u for entry %u\n", rpl->status, GET_TID(rpl)); m_freem(m); return (0); } void t3_init_l2t_cpl_handlers(struct adapter *sc) { t3_register_cpl_handler(sc, CPL_L2T_WRITE_RPL, do_l2t_write_rpl); } #endif Index: projects/clang380-import/sys/dev/cxgbe/tom/t4_tom_l2t.c =================================================================== --- projects/clang380-import/sys/dev/cxgbe/tom/t4_tom_l2t.c (revision 293015) +++ projects/clang380-import/sys/dev/cxgbe/tom/t4_tom_l2t.c (revision 293016) @@ -1,471 +1,471 @@ /*- * Copyright (c) 2012 Chelsio Communications, 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. * 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$"); #include "opt_inet.h" #include "opt_inet6.h" #ifdef TCP_OFFLOAD #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/common.h" #include "common/t4_msg.h" #include "tom/t4_tom_l2t.h" #include "tom/t4_tom.h" #define VLAN_NONE 0xfff static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) { if (atomic_fetchadd_int(&e->refcnt, 1) == 0) /* 0 -> 1 transition */ atomic_subtract_int(&d->nfree, 1); } static inline u_int l2_hash(struct l2t_data *d, const struct sockaddr *sa, int ifindex) { u_int hash, half = d->l2t_size / 2, start = 0; const void *key; size_t len; KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6, ("%s: sa %p has unexpected sa_family %d", __func__, sa, sa->sa_family)); if (sa->sa_family == AF_INET) { const struct sockaddr_in *sin = (const void *)sa; key = &sin->sin_addr; len = sizeof(sin->sin_addr); } else { const struct sockaddr_in6 *sin6 = (const void *)sa; key = &sin6->sin6_addr; len = sizeof(sin6->sin6_addr); start = half; } hash = fnv_32_buf(key, len, FNV1_32_INIT); hash = fnv_32_buf(&ifindex, sizeof(ifindex), hash); hash %= half; return (hash + start); } static inline int l2_cmp(const struct sockaddr *sa, struct l2t_entry *e) { KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6, ("%s: sa %p has unexpected sa_family %d", __func__, sa, sa->sa_family)); if (sa->sa_family == AF_INET) { const struct sockaddr_in *sin = (const void *)sa; return (e->addr[0] != sin->sin_addr.s_addr); } else { const struct sockaddr_in6 *sin6 = (const void *)sa; return (memcmp(&e->addr[0], &sin6->sin6_addr, sizeof(e->addr))); } } static inline void l2_store(const struct sockaddr *sa, struct l2t_entry *e) { KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6, ("%s: sa %p has unexpected sa_family %d", __func__, sa, sa->sa_family)); if (sa->sa_family == AF_INET) { const struct sockaddr_in *sin = (const void *)sa; e->addr[0] = sin->sin_addr.s_addr; e->ipv6 = 0; } else { const struct sockaddr_in6 *sin6 = (const void *)sa; memcpy(&e->addr[0], &sin6->sin6_addr, sizeof(e->addr)); e->ipv6 = 1; } } /* * Add a WR to an L2T entry's queue of work requests awaiting resolution. * Must be called with the entry's lock held. */ static inline void arpq_enqueue(struct l2t_entry *e, struct wrqe *wr) { mtx_assert(&e->lock, MA_OWNED); STAILQ_INSERT_TAIL(&e->wr_list, wr, link); } static inline void send_pending(struct adapter *sc, struct l2t_entry *e) { struct wrqe *wr; mtx_assert(&e->lock, MA_OWNED); while ((wr = STAILQ_FIRST(&e->wr_list)) != NULL) { STAILQ_REMOVE_HEAD(&e->wr_list, link); t4_wrq_tx(sc, wr); } } static void resolution_failed(struct adapter *sc, struct l2t_entry *e) { struct tom_data *td = sc->tom_softc; mtx_assert(&e->lock, MA_OWNED); mtx_lock(&td->unsent_wr_lock); STAILQ_CONCAT(&td->unsent_wr_list, &e->wr_list); mtx_unlock(&td->unsent_wr_lock); taskqueue_enqueue(taskqueue_thread, &td->reclaim_wr_resources); } static void update_entry(struct adapter *sc, struct l2t_entry *e, uint8_t *lladdr, uint16_t vtag) { mtx_assert(&e->lock, MA_OWNED); /* * The entry may be in active use (e->refcount > 0) or not. We update * it even when it's not as this simplifies the case where we decide to * reuse the entry later. */ if (lladdr == NULL && (e->state == L2T_STATE_RESOLVING || e->state == L2T_STATE_FAILED)) { /* * Never got a valid L2 address for this one. Just mark it as * failed instead of removing it from the hash (for which we'd * need to wlock the table). */ e->state = L2T_STATE_FAILED; resolution_failed(sc, e); return; } else if (lladdr == NULL) { /* Valid or already-stale entry was deleted (or expired) */ KASSERT(e->state == L2T_STATE_VALID || e->state == L2T_STATE_STALE, ("%s: lladdr NULL, state %d", __func__, e->state)); e->state = L2T_STATE_STALE; } else { if (e->state == L2T_STATE_RESOLVING || e->state == L2T_STATE_FAILED || memcmp(e->dmac, lladdr, ETHER_ADDR_LEN)) { /* unresolved -> resolved; or dmac changed */ memcpy(e->dmac, lladdr, ETHER_ADDR_LEN); e->vlan = vtag; t4_write_l2e(sc, e, 1); } e->state = L2T_STATE_VALID; } } static int resolve_entry(struct adapter *sc, struct l2t_entry *e) { struct tom_data *td = sc->tom_softc; struct toedev *tod = &td->tod; struct sockaddr_in sin = {0}; struct sockaddr_in6 sin6 = {0}; struct sockaddr *sa; - uint8_t dmac[ETHER_ADDR_LEN]; + uint8_t dmac[ETHER_HDR_LEN]; uint16_t vtag = VLAN_NONE; int rc; if (e->ipv6 == 0) { sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_addr.s_addr = e->addr[0]; sa = (void *)&sin; } else { sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); memcpy(&sin6.sin6_addr, &e->addr[0], sizeof(e->addr)); sa = (void *)&sin6; } rc = toe_l2_resolve(tod, e->ifp, sa, dmac, &vtag); if (rc == EWOULDBLOCK) return (rc); mtx_lock(&e->lock); update_entry(sc, e, rc == 0 ? dmac : NULL, vtag); mtx_unlock(&e->lock); return (rc); } int t4_l2t_send_slow(struct adapter *sc, struct wrqe *wr, struct l2t_entry *e) { again: switch (e->state) { case L2T_STATE_STALE: /* entry is stale, kick off revalidation */ if (resolve_entry(sc, e) != EWOULDBLOCK) goto again; /* entry updated, re-examine state */ /* Fall through */ case L2T_STATE_VALID: /* fast-path, send the packet on */ t4_wrq_tx(sc, wr); return (0); case L2T_STATE_RESOLVING: case L2T_STATE_SYNC_WRITE: mtx_lock(&e->lock); if (e->state != L2T_STATE_SYNC_WRITE && e->state != L2T_STATE_RESOLVING) { /* state changed by the time we got here */ mtx_unlock(&e->lock); goto again; } arpq_enqueue(e, wr); mtx_unlock(&e->lock); if (resolve_entry(sc, e) == EWOULDBLOCK) break; mtx_lock(&e->lock); if (e->state == L2T_STATE_VALID && !STAILQ_EMPTY(&e->wr_list)) send_pending(sc, e); if (e->state == L2T_STATE_FAILED) resolution_failed(sc, e); mtx_unlock(&e->lock); break; case L2T_STATE_FAILED: return (EHOSTUNREACH); } return (0); } /* * Called when an L2T entry has no more users. The entry is left in the hash * table since it is likely to be reused but we also bump nfree to indicate * that the entry can be reallocated for a different neighbor. We also drop * the existing neighbor reference in case the neighbor is going away and is * waiting on our reference. * * Because entries can be reallocated to other neighbors once their ref count * drops to 0 we need to take the entry's lock to avoid races with a new * incarnation. */ static int do_l2t_write_rpl2(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) { struct adapter *sc = iq->adapter; const struct cpl_l2t_write_rpl *rpl = (const void *)(rss + 1); unsigned int tid = GET_TID(rpl); unsigned int idx = tid % L2T_SIZE; int rc; rc = do_l2t_write_rpl(iq, rss, m); if (rc != 0) return (rc); if (tid & F_SYNC_WR) { struct l2t_entry *e = &sc->l2t->l2tab[idx - sc->vres.l2t.start]; mtx_lock(&e->lock); if (e->state != L2T_STATE_SWITCHING) { send_pending(sc, e); e->state = L2T_STATE_VALID; } mtx_unlock(&e->lock); } return (0); } void t4_init_l2t_cpl_handlers(struct adapter *sc) { t4_register_cpl_handler(sc, CPL_L2T_WRITE_RPL, do_l2t_write_rpl2); } void t4_uninit_l2t_cpl_handlers(struct adapter *sc) { t4_register_cpl_handler(sc, CPL_L2T_WRITE_RPL, do_l2t_write_rpl); } /* * The TOE wants an L2 table entry that it can use to reach the next hop over * the specified port. Produce such an entry - create one if needed. * * Note that the ifnet could be a pseudo-device like if_vlan, if_lagg, etc. on * top of the real cxgbe interface. */ struct l2t_entry * t4_l2t_get(struct port_info *pi, struct ifnet *ifp, struct sockaddr *sa) { struct l2t_entry *e; struct l2t_data *d = pi->adapter->l2t; u_int hash, smt_idx = pi->port_id; KASSERT(sa->sa_family == AF_INET || sa->sa_family == AF_INET6, ("%s: sa %p has unexpected sa_family %d", __func__, sa, sa->sa_family)); #ifndef VLAN_TAG if (ifp->if_type == IFT_L2VLAN) return (NULL); #endif hash = l2_hash(d, sa, ifp->if_index); rw_wlock(&d->lock); for (e = d->l2tab[hash].first; e; e = e->next) { if (l2_cmp(sa, e) == 0 && e->ifp == ifp && e->smt_idx == smt_idx) { l2t_hold(d, e); goto done; } } /* Need to allocate a new entry */ e = t4_alloc_l2e(d); if (e) { mtx_lock(&e->lock); /* avoid race with t4_l2t_free */ e->next = d->l2tab[hash].first; d->l2tab[hash].first = e; e->state = L2T_STATE_RESOLVING; l2_store(sa, e); e->ifp = ifp; e->smt_idx = smt_idx; e->hash = hash; e->lport = pi->lport; atomic_store_rel_int(&e->refcnt, 1); #ifdef VLAN_TAG if (ifp->if_type == IFT_L2VLAN) VLAN_TAG(ifp, &e->vlan); else e->vlan = VLAN_NONE; #endif mtx_unlock(&e->lock); } done: rw_wunlock(&d->lock); return e; } /* * Called when the host's ARP layer makes a change to some entry that is loaded * into the HW L2 table. */ void t4_l2_update(struct toedev *tod, struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr, uint16_t vtag) { struct adapter *sc = tod->tod_softc; struct l2t_entry *e; struct l2t_data *d = sc->l2t; u_int hash; KASSERT(d != NULL, ("%s: no L2 table", __func__)); hash = l2_hash(d, sa, ifp->if_index); rw_rlock(&d->lock); for (e = d->l2tab[hash].first; e; e = e->next) { if (l2_cmp(sa, e) == 0 && e->ifp == ifp) { mtx_lock(&e->lock); if (atomic_load_acq_int(&e->refcnt)) goto found; e->state = L2T_STATE_STALE; mtx_unlock(&e->lock); break; } } rw_runlock(&d->lock); /* * This is of no interest to us. We've never had an offloaded * connection to this destination, and we aren't attempting one right * now. */ return; found: rw_runlock(&d->lock); KASSERT(e->state != L2T_STATE_UNUSED, ("%s: unused entry in the hash.", __func__)); update_entry(sc, e, lladdr, vtag); mtx_unlock(&e->lock); } #endif Index: projects/clang380-import/sys/dev/rtwn/if_rtwn.c =================================================================== --- projects/clang380-import/sys/dev/rtwn/if_rtwn.c (nonexistent) +++ projects/clang380-import/sys/dev/rtwn/if_rtwn.c (revision 293016) @@ -0,0 +1,3490 @@ +/* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini + * Copyright (c) 2015 Stefan Sperling + * + * 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$"); + +/* + * Driver for Realtek RTL8188CE + */ + +#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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define RTWN_DEBUG +#ifdef RTWN_DEBUG +#define DPRINTF(x) do { if (sc->sc_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (sc->sc_debug >= (n)) printf x; } while (0) +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +/* + * PCI configuration space registers. + */ +#define RTWN_PCI_IOBA 0x10 /* i/o mapped base */ +#define RTWN_PCI_MMBA 0x18 /* memory mapped base */ + +#define RTWN_INT_ENABLE (R92C_IMR_ROK | R92C_IMR_VODOK | R92C_IMR_VIDOK | \ + R92C_IMR_BEDOK | R92C_IMR_BKDOK | R92C_IMR_MGNTDOK | \ + R92C_IMR_HIGHDOK | R92C_IMR_BDOK | R92C_IMR_RDU | \ + R92C_IMR_RXFOVW) + +struct rtwn_ident { + uint16_t vendor; + uint16_t device; + const char *name; +}; + + +static const struct rtwn_ident rtwn_ident_table[] = { + { 0x10ec, 0x8176, "Realtek RTL8188CE" }, + { 0, 0, NULL } +}; + + +static void rtwn_dma_map_addr(void *, bus_dma_segment_t *, int, int); +static void rtwn_setup_rx_desc(struct rtwn_softc *, struct r92c_rx_desc *, + bus_addr_t, size_t, int); +static int rtwn_alloc_rx_list(struct rtwn_softc *); +static void rtwn_reset_rx_list(struct rtwn_softc *); +static void rtwn_free_rx_list(struct rtwn_softc *); +static int rtwn_alloc_tx_list(struct rtwn_softc *, int); +static void rtwn_reset_tx_list(struct rtwn_softc *, int); +static void rtwn_free_tx_list(struct rtwn_softc *, int); +static struct ieee80211vap *rtwn_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void rtwn_vap_delete(struct ieee80211vap *); +static void rtwn_write_1(struct rtwn_softc *, uint16_t, uint8_t); +static void rtwn_write_2(struct rtwn_softc *, uint16_t, uint16_t); +static void rtwn_write_4(struct rtwn_softc *, uint16_t, uint32_t); +static uint8_t rtwn_read_1(struct rtwn_softc *, uint16_t); +static uint16_t rtwn_read_2(struct rtwn_softc *, uint16_t); +static uint32_t rtwn_read_4(struct rtwn_softc *, uint16_t); +static int rtwn_fw_cmd(struct rtwn_softc *, uint8_t, const void *, int); +static void rtwn_rf_write(struct rtwn_softc *, int, uint8_t, uint32_t); +static uint32_t rtwn_rf_read(struct rtwn_softc *, int, uint8_t); +static int rtwn_llt_write(struct rtwn_softc *, uint32_t, uint32_t); +static uint8_t rtwn_efuse_read_1(struct rtwn_softc *, uint16_t); +static void rtwn_efuse_read(struct rtwn_softc *); +static int rtwn_read_chipid(struct rtwn_softc *); +static void rtwn_read_rom(struct rtwn_softc *); +static int rtwn_ra_init(struct rtwn_softc *); +static void rtwn_tsf_sync_enable(struct rtwn_softc *); +static void rtwn_set_led(struct rtwn_softc *, int, int); +static void rtwn_calib_to(void *); +static int rtwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int rtwn_updateedca(struct ieee80211com *); +static void rtwn_update_avgrssi(struct rtwn_softc *, int, int8_t); +static int8_t rtwn_get_rssi(struct rtwn_softc *, int, void *); +static void rtwn_rx_frame(struct rtwn_softc *, struct r92c_rx_desc *, + struct rtwn_rx_data *, int); +static int rtwn_tx(struct rtwn_softc *, struct mbuf *, + struct ieee80211_node *); +static void rtwn_tx_done(struct rtwn_softc *, int); +static int rtwn_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int rtwn_transmit(struct ieee80211com *, struct mbuf *); +static void rtwn_parent(struct ieee80211com *); +static void rtwn_start(struct rtwn_softc *sc); +static void rtwn_watchdog(void *); +static int rtwn_power_on(struct rtwn_softc *); +static int rtwn_llt_init(struct rtwn_softc *); +static void rtwn_fw_reset(struct rtwn_softc *); +static void rtwn_fw_loadpage(struct rtwn_softc *, int, const uint8_t *, + int); +static int rtwn_load_firmware(struct rtwn_softc *); +static int rtwn_dma_init(struct rtwn_softc *); +static void rtwn_mac_init(struct rtwn_softc *); +static void rtwn_bb_init(struct rtwn_softc *); +static void rtwn_rf_init(struct rtwn_softc *); +static void rtwn_cam_init(struct rtwn_softc *); +static void rtwn_pa_bias_init(struct rtwn_softc *); +static void rtwn_rxfilter_init(struct rtwn_softc *); +static void rtwn_edca_init(struct rtwn_softc *); +static void rtwn_write_txpower(struct rtwn_softc *, int, uint16_t[]); +static void rtwn_get_txpower(struct rtwn_softc *, int, + struct ieee80211_channel *, struct ieee80211_channel *, + uint16_t[]); +static void rtwn_set_txpower(struct rtwn_softc *, + struct ieee80211_channel *, struct ieee80211_channel *); +static void rtwn_scan_start(struct ieee80211com *); +static void rtwn_scan_end(struct ieee80211com *); +static void rtwn_set_channel(struct ieee80211com *); +static void rtwn_update_mcast(struct ieee80211com *); +static void rtwn_set_chan(struct rtwn_softc *, + struct ieee80211_channel *, struct ieee80211_channel *); +static int rtwn_iq_calib_chain(struct rtwn_softc *, int, uint16_t[2], + uint16_t[2]); +static void rtwn_iq_calib_run(struct rtwn_softc *, int, uint16_t[2][2], + uint16_t[2][2]); +static int rtwn_iq_calib_compare_results(uint16_t[2][2], uint16_t[2][2], + uint16_t[2][2], uint16_t[2][2], int); +static void rtwn_iq_calib_write_results(struct rtwn_softc *, uint16_t[2], + uint16_t[2], int); +static void rtwn_iq_calib(struct rtwn_softc *); +static void rtwn_lc_calib(struct rtwn_softc *); +static void rtwn_temp_calib(struct rtwn_softc *); +static void rtwn_init_locked(struct rtwn_softc *); +static void rtwn_init(struct rtwn_softc *); +static void rtwn_stop_locked(struct rtwn_softc *); +static void rtwn_stop(struct rtwn_softc *); +static void rtwn_intr(void *); +static void rtwn_hw_reset(void *, int); + +/* Aliases. */ +#define rtwn_bb_write rtwn_write_4 +#define rtwn_bb_read rtwn_read_4 + +static int rtwn_probe(device_t); +static int rtwn_attach(device_t); +static int rtwn_detach(device_t); +static int rtwn_shutdown(device_t); +static int rtwn_suspend(device_t); +static int rtwn_resume(device_t); + +static device_method_t rtwn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rtwn_probe), + DEVMETHOD(device_attach, rtwn_attach), + DEVMETHOD(device_detach, rtwn_detach), + DEVMETHOD(device_shutdown, rtwn_shutdown), + DEVMETHOD(device_suspend, rtwn_suspend), + DEVMETHOD(device_resume, rtwn_resume), + + DEVMETHOD_END +}; + +static driver_t rtwn_driver = { + "rtwn", + rtwn_methods, + sizeof (struct rtwn_softc) +}; +static devclass_t rtwn_devclass; + +DRIVER_MODULE(rtwn, pci, rtwn_driver, rtwn_devclass, NULL, NULL); + +MODULE_VERSION(rtwn, 1); + +MODULE_DEPEND(rtwn, pci, 1, 1, 1); +MODULE_DEPEND(rtwn, wlan, 1, 1, 1); +MODULE_DEPEND(rtwn, firmware, 1, 1, 1); + +static int +rtwn_probe(device_t dev) +{ + const struct rtwn_ident *ident; + + for (ident = rtwn_ident_table; ident->name != NULL; ident++) { + if (pci_get_vendor(dev) == ident->vendor && + pci_get_device(dev) == ident->device) { + device_set_desc(dev, ident->name); + return (BUS_PROBE_DEFAULT); + } + } + return (ENXIO); +} + +static int +rtwn_attach(device_t dev) +{ + struct rtwn_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t lcsr; + uint8_t bands; + int i, count, error, rid; + + sc->sc_dev = dev; + sc->sc_debug = 0; + + /* + * Get the offset of the PCI Express Capability Structure in PCI + * Configuration Space. + */ + error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); + if (error != 0) { + device_printf(dev, "PCIe capability structure not found!\n"); + return (error); + } + + /* Enable bus-mastering. */ + pci_enable_busmaster(dev); + + rid = PCIR_BAR(2); + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem == NULL) { + device_printf(dev, "can't map mem space\n"); + return (ENOMEM); + } + sc->sc_st = rman_get_bustag(sc->mem); + sc->sc_sh = rman_get_bushandle(sc->mem); + + /* Install interrupt handler. */ + count = 1; + rid = 0; + if (pci_alloc_msi(dev, &count) == 0) + rid = 1; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | + (rid != 0 ? 0 : RF_SHAREABLE)); + if (sc->irq == NULL) { + device_printf(dev, "can't map interrupt\n"); + return (ENXIO); + } + + RTWN_LOCK_INIT(sc); + callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); + callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_reinit_task, 0, rtwn_hw_reset, sc); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + error = rtwn_read_chipid(sc); + if (error != 0) { + device_printf(dev, "unsupported test chip\n"); + goto fail; + } + + /* Disable PCIe Active State Power Management (ASPM). */ + lcsr = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 4); + lcsr &= ~PCIEM_LINK_CTL_ASPMC; + pci_write_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, lcsr, 4); + + /* Allocate Tx/Rx buffers. */ + error = rtwn_alloc_rx_list(sc); + if (error != 0) { + device_printf(dev, "could not allocate Rx buffers\n"); + goto fail; + } + for (i = 0; i < RTWN_NTXQUEUES; i++) { + error = rtwn_alloc_tx_list(sc, i); + if (error != 0) { + device_printf(dev, "could not allocate Tx buffers\n"); + goto fail; + } + } + + /* Determine number of Tx/Rx chains. */ + if (sc->chip & RTWN_CHIP_92C) { + sc->ntxchains = (sc->chip & RTWN_CHIP_92C_1T2R) ? 1 : 2; + sc->nrxchains = 2; + } else { + sc->ntxchains = 1; + sc->nrxchains = 1; + } + rtwn_read_rom(sc); + + device_printf(sc->sc_dev, "MAC/BB RTL%s, RF 6052 %dT%dR\n", + (sc->chip & RTWN_CHIP_92C) ? "8192CE" : "8188CE", + sc->ntxchains, sc->nrxchains); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_WME /* 802.11e */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic); + + ic->ic_wme.wme_update = rtwn_updateedca; + ic->ic_update_mcast = rtwn_update_mcast; + ic->ic_scan_start =rtwn_scan_start; + ic->ic_scan_end = rtwn_scan_end; + ic->ic_set_channel = rtwn_set_channel; + ic->ic_raw_xmit = rtwn_raw_xmit; + ic->ic_transmit = rtwn_transmit; + ic->ic_parent = rtwn_parent; + ic->ic_vap_create = rtwn_vap_create; + ic->ic_vap_delete = rtwn_vap_delete; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RTWN_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RTWN_RX_RADIOTAP_PRESENT); + + /* + * Hook our interrupt after all initialization is complete. + */ + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, rtwn_intr, sc, &sc->sc_ih); + if (error != 0) { + device_printf(dev, "can't establish interrupt, error %d\n", + error); + goto fail; + } + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +fail: + rtwn_detach(dev); + return (error); +} + + +static int +rtwn_detach(device_t dev) +{ + struct rtwn_softc *sc = device_get_softc(dev); + int i; + + if (sc->sc_ic.ic_softc != NULL) { + ieee80211_draintask(&sc->sc_ic, &sc->sc_reinit_task); + rtwn_stop(sc); + + callout_drain(&sc->calib_to); + callout_drain(&sc->watchdog_to); + ieee80211_ifdetach(&sc->sc_ic); + mbufq_drain(&sc->sc_snd); + } + + /* Uninstall interrupt handler. */ + if (sc->irq != NULL) { + bus_teardown_intr(dev, sc->irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), + sc->irq); + pci_release_msi(dev); + } + + /* Free Tx/Rx buffers. */ + for (i = 0; i < RTWN_NTXQUEUES; i++) + rtwn_free_tx_list(sc, i); + rtwn_free_rx_list(sc); + + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem), sc->mem); + + RTWN_LOCK_DESTROY(sc); + return (0); +} + +static int +rtwn_shutdown(device_t dev) +{ + + return (0); +} + +static int +rtwn_suspend(device_t dev) +{ + return (0); +} + +static int +rtwn_resume(device_t dev) +{ + + return (0); +} + +static void +rtwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + + if (error != 0) + return; + KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +static void +rtwn_setup_rx_desc(struct rtwn_softc *sc, struct r92c_rx_desc *desc, + bus_addr_t addr, size_t len, int idx) +{ + + memset(desc, 0, sizeof(*desc)); + desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) | + ((idx == RTWN_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0)); + desc->rxbufaddr = htole32(addr); + bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize, + BUS_SPACE_BARRIER_WRITE); + desc->rxdw0 |= htole32(R92C_RXDW0_OWN); +} + +static int +rtwn_alloc_rx_list(struct rtwn_softc *sc) +{ + struct rtwn_rx_ring *rx_ring = &sc->rx_ring; + struct rtwn_rx_data *rx_data; + bus_size_t size; + int i, error; + + /* Allocate Rx descriptors. */ + size = sizeof(struct r92c_rx_desc) * RTWN_RX_LIST_COUNT; + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create rx desc DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc, + BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, + &rx_ring->desc_map); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate rx desc\n"); + goto fail; + } + error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map, + rx_ring->desc, size, rtwn_dma_map_addr, &rx_ring->paddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load rx desc DMA map\n"); + goto fail; + } + bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, + BUS_DMASYNC_PREWRITE); + + /* Create RX buffer DMA tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + 1, MCLBYTES, 0, NULL, NULL, &rx_ring->data_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); + goto fail; + } + + /* Allocate Rx buffers. */ + for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { + rx_data = &rx_ring->rx_data[i]; + error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create rx buf DMA map\n"); + goto fail; + } + + rx_data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (rx_data->m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map, + mtod(rx_data->m, void *), MCLBYTES, rtwn_dma_map_addr, + &rx_data->paddr, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load rx buf DMA map"); + goto fail; + } + + rtwn_setup_rx_desc(sc, &rx_ring->desc[i], rx_data->paddr, + MCLBYTES, i); + } + return (0); + +fail: + rtwn_free_rx_list(sc); + return (error); +} + +static void +rtwn_reset_rx_list(struct rtwn_softc *sc) +{ + struct rtwn_rx_ring *rx_ring = &sc->rx_ring; + struct rtwn_rx_data *rx_data; + int i; + + for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { + rx_data = &rx_ring->rx_data[i]; + rtwn_setup_rx_desc(sc, &rx_ring->desc[i], rx_data->paddr, + MCLBYTES, i); + } +} + +static void +rtwn_free_rx_list(struct rtwn_softc *sc) +{ + struct rtwn_rx_ring *rx_ring = &sc->rx_ring; + struct rtwn_rx_data *rx_data; + int i; + + if (rx_ring->desc_dmat != NULL) { + if (rx_ring->desc != NULL) { + bus_dmamap_unload(rx_ring->desc_dmat, + rx_ring->desc_map); + bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc, + rx_ring->desc_map); + rx_ring->desc = NULL; + } + bus_dma_tag_destroy(rx_ring->desc_dmat); + rx_ring->desc_dmat = NULL; + } + + for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { + rx_data = &rx_ring->rx_data[i]; + + if (rx_data->m != NULL) { + bus_dmamap_unload(rx_ring->data_dmat, rx_data->map); + m_freem(rx_data->m); + rx_data->m = NULL; + } + bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map); + rx_data->map = NULL; + } + if (rx_ring->data_dmat != NULL) { + bus_dma_tag_destroy(rx_ring->data_dmat); + rx_ring->data_dmat = NULL; + } +} + +static int +rtwn_alloc_tx_list(struct rtwn_softc *sc, int qid) +{ + struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; + struct rtwn_tx_data *tx_data; + bus_size_t size; + int i, error; + + size = sizeof(struct r92c_tx_desc) * RTWN_TX_LIST_COUNT; + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(tx_ring->desc_dmat, (void **)&tx_ring->desc, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map); + if (error != 0) { + device_printf(sc->sc_dev, "can't map tx ring DMA memory\n"); + goto fail; + } + error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map, + tx_ring->desc, size, rtwn_dma_map_addr, &tx_ring->paddr, + BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, "could not load desc DMA map\n"); + goto fail; + } + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + 1, MCLBYTES, 0, NULL, NULL, &tx_ring->data_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); + goto fail; + } + + for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { + struct r92c_tx_desc *desc = &tx_ring->desc[i]; + + /* setup tx desc */ + desc->nextdescaddr = htole32(tx_ring->paddr + + + sizeof(struct r92c_tx_desc) + * ((i + 1) % RTWN_TX_LIST_COUNT)); + tx_data = &tx_ring->tx_data[i]; + error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map); + if (error != 0) { + device_printf(sc->sc_dev, + "could not create tx buf DMA map\n"); + goto fail; + } + tx_data->m = NULL; + tx_data->ni = NULL; + } + return (0); + +fail: + rtwn_free_tx_list(sc, qid); + return (error); +} + +static void +rtwn_reset_tx_list(struct rtwn_softc *sc, int qid) +{ + struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; + int i; + + for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { + struct r92c_tx_desc *desc = &tx_ring->desc[i]; + struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; + + memset(desc, 0, sizeof(*desc) - + (sizeof(desc->reserved) + sizeof(desc->nextdescaddr64) + + sizeof(desc->nextdescaddr))); + + if (tx_data->m != NULL) { + bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); + m_freem(tx_data->m); + tx_data->m = NULL; + } + if (tx_data->ni != NULL) { + ieee80211_free_node(tx_data->ni); + tx_data->ni = NULL; + } + } + + bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, + BUS_DMASYNC_POSTWRITE); + + sc->qfullmsk &= ~(1 << qid); + tx_ring->queued = 0; + tx_ring->cur = 0; +} + +static void +rtwn_free_tx_list(struct rtwn_softc *sc, int qid) +{ + struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; + struct rtwn_tx_data *tx_data; + int i; + + if (tx_ring->desc_dmat != NULL) { + if (tx_ring->desc != NULL) { + bus_dmamap_unload(tx_ring->desc_dmat, + tx_ring->desc_map); + bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc, + tx_ring->desc_map); + } + bus_dma_tag_destroy(tx_ring->desc_dmat); + } + + for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { + tx_data = &tx_ring->tx_data[i]; + + if (tx_data->m != NULL) { + bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); + m_freem(tx_data->m); + tx_data->m = NULL; + } + } + if (tx_ring->data_dmat != NULL) { + bus_dma_tag_destroy(tx_ring->data_dmat); + tx_ring->data_dmat = NULL; + } + + sc->qfullmsk &= ~(1 << qid); + tx_ring->queued = 0; + tx_ring->cur = 0; +} + + +static struct ieee80211vap * +rtwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct rtwn_vap *rvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) + return (NULL); + + rvp = malloc(sizeof(struct rtwn_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &rvp->vap; + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(rvp, M_80211_VAP); + return (NULL); + } + + /* Override state transition machine. */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = rtwn_newstate; + + /* Complete setup. */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return (vap); +} + +static void +rtwn_vap_delete(struct ieee80211vap *vap) +{ + struct rtwn_vap *rvp = RTWN_VAP(vap); + + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); +} + +static void +rtwn_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val) +{ + + bus_space_write_1(sc->sc_st, sc->sc_sh, addr, val); +} + +static void +rtwn_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val) +{ + + val = htole16(val); + bus_space_write_2(sc->sc_st, sc->sc_sh, addr, val); +} + +static void +rtwn_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val) +{ + + val = htole32(val); + bus_space_write_4(sc->sc_st, sc->sc_sh, addr, val); +} + +static uint8_t +rtwn_read_1(struct rtwn_softc *sc, uint16_t addr) +{ + + return (bus_space_read_1(sc->sc_st, sc->sc_sh, addr)); +} + +static uint16_t +rtwn_read_2(struct rtwn_softc *sc, uint16_t addr) +{ + + return (bus_space_read_2(sc->sc_st, sc->sc_sh, addr)); +} + +static uint32_t +rtwn_read_4(struct rtwn_softc *sc, uint16_t addr) +{ + + return (bus_space_read_4(sc->sc_st, sc->sc_sh, addr)); +} + +static int +rtwn_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len) +{ + struct r92c_fw_cmd cmd; + int ntries; + + /* Wait for current FW box to be empty. */ + for (ntries = 0; ntries < 100; ntries++) { + if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) + break; + DELAY(1); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "could not send firmware command %d\n", id); + return (ETIMEDOUT); + } + memset(&cmd, 0, sizeof(cmd)); + cmd.id = id; + if (len > 3) + cmd.id |= R92C_CMD_FLAG_EXT; + KASSERT(len <= sizeof(cmd.msg), ("rtwn_fw_cmd\n")); + memcpy(cmd.msg, buf, len); + + /* Write the first word last since that will trigger the FW. */ + rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), *((uint8_t *)&cmd + 4)); + rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *((uint8_t *)&cmd + 0)); + + sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; + + /* Give firmware some time for processing. */ + DELAY(2000); + + return (0); +} + +static void +rtwn_rf_write(struct rtwn_softc *sc, int chain, uint8_t addr, uint32_t val) +{ + rtwn_bb_write(sc, R92C_LSSI_PARAM(chain), + SM(R92C_LSSI_PARAM_ADDR, addr) | + SM(R92C_LSSI_PARAM_DATA, val)); +} + +static uint32_t +rtwn_rf_read(struct rtwn_softc *sc, int chain, uint8_t addr) +{ + uint32_t reg[R92C_MAX_CHAINS], val; + + reg[0] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)); + if (chain != 0) + reg[chain] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(chain)); + + rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), + reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE); + DELAY(1000); + + rtwn_bb_write(sc, R92C_HSSI_PARAM2(chain), + RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) | + R92C_HSSI_PARAM2_READ_EDGE); + DELAY(1000); + + rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), + reg[0] | R92C_HSSI_PARAM2_READ_EDGE); + DELAY(1000); + + if (rtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI) + val = rtwn_bb_read(sc, R92C_HSPI_READBACK(chain)); + else + val = rtwn_bb_read(sc, R92C_LSSI_READBACK(chain)); + return (MS(val, R92C_LSSI_READBACK_DATA)); +} + +static int +rtwn_llt_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data) +{ + int ntries; + + rtwn_write_4(sc, R92C_LLT_INIT, + SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) | + SM(R92C_LLT_INIT_ADDR, addr) | + SM(R92C_LLT_INIT_DATA, data)); + /* Wait for write operation to complete. */ + for (ntries = 0; ntries < 20; ntries++) { + if (MS(rtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) == + R92C_LLT_INIT_OP_NO_ACTIVE) + return (0); + DELAY(5); + } + return (ETIMEDOUT); +} + +static uint8_t +rtwn_efuse_read_1(struct rtwn_softc *sc, uint16_t addr) +{ + uint32_t reg; + int ntries; + + reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); + reg = RW(reg, R92C_EFUSE_CTRL_ADDR, addr); + reg &= ~R92C_EFUSE_CTRL_VALID; + rtwn_write_4(sc, R92C_EFUSE_CTRL, reg); + /* Wait for read operation to complete. */ + for (ntries = 0; ntries < 100; ntries++) { + reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); + if (reg & R92C_EFUSE_CTRL_VALID) + return (MS(reg, R92C_EFUSE_CTRL_DATA)); + DELAY(5); + } + device_printf(sc->sc_dev, + "could not read efuse byte at address 0x%x\n", addr); + return (0xff); +} + +static void +rtwn_efuse_read(struct rtwn_softc *sc) +{ + uint8_t *rom = (uint8_t *)&sc->rom; + uint16_t addr = 0; + uint32_t reg; + uint8_t off, msk; + int i; + + reg = rtwn_read_2(sc, R92C_SYS_ISO_CTRL); + if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) { + rtwn_write_2(sc, R92C_SYS_ISO_CTRL, + reg | R92C_SYS_ISO_CTRL_PWC_EV12V); + } + reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); + if (!(reg & R92C_SYS_FUNC_EN_ELDR)) { + rtwn_write_2(sc, R92C_SYS_FUNC_EN, + reg | R92C_SYS_FUNC_EN_ELDR); + } + reg = rtwn_read_2(sc, R92C_SYS_CLKR); + if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) != + (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) { + rtwn_write_2(sc, R92C_SYS_CLKR, + reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M); + } + memset(&sc->rom, 0xff, sizeof(sc->rom)); + while (addr < 512) { + reg = rtwn_efuse_read_1(sc, addr); + if (reg == 0xff) + break; + addr++; + off = reg >> 4; + msk = reg & 0xf; + for (i = 0; i < 4; i++) { + if (msk & (1 << i)) + continue; + rom[off * 8 + i * 2 + 0] = + rtwn_efuse_read_1(sc, addr); + addr++; + rom[off * 8 + i * 2 + 1] = + rtwn_efuse_read_1(sc, addr); + addr++; + } + } +#ifdef RTWN_DEBUG + if (sc->sc_debug >= 2) { + /* Dump ROM content. */ + printf("\n"); + for (i = 0; i < sizeof(sc->rom); i++) + printf("%02x:", rom[i]); + printf("\n"); + } +#endif +} + +static int +rtwn_read_chipid(struct rtwn_softc *sc) +{ + uint32_t reg; + + reg = rtwn_read_4(sc, R92C_SYS_CFG); + if (reg & R92C_SYS_CFG_TRP_VAUX_EN) + /* Unsupported test chip. */ + return (EIO); + + if (reg & R92C_SYS_CFG_TYPE_92C) { + sc->chip |= RTWN_CHIP_92C; + /* Check if it is a castrated 8192C. */ + if (MS(rtwn_read_4(sc, R92C_HPON_FSM), + R92C_HPON_FSM_CHIP_BONDING_ID) == + R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R) + sc->chip |= RTWN_CHIP_92C_1T2R; + } + if (reg & R92C_SYS_CFG_VENDOR_UMC) { + sc->chip |= RTWN_CHIP_UMC; + if (MS(reg, R92C_SYS_CFG_CHIP_VER_RTL) == 0) + sc->chip |= RTWN_CHIP_UMC_A_CUT; + } + return (0); +} + +static void +rtwn_read_rom(struct rtwn_softc *sc) +{ + struct r92c_rom *rom = &sc->rom; + + /* Read full ROM image. */ + rtwn_efuse_read(sc); + + if (rom->id != 0x8129) + device_printf(sc->sc_dev, "invalid EEPROM ID 0x%x\n", rom->id); + + /* XXX Weird but this is what the vendor driver does. */ + sc->pa_setting = rtwn_efuse_read_1(sc, 0x1fa); + DPRINTF(("PA setting=0x%x\n", sc->pa_setting)); + + sc->board_type = MS(rom->rf_opt1, R92C_ROM_RF1_BOARD_TYPE); + + sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY); + DPRINTF(("regulatory type=%d\n", sc->regulatory)); + + IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, rom->macaddr); +} + +/* + * Initialize rate adaptation in firmware. + */ +static int +rtwn_ra_init(struct rtwn_softc *sc) +{ + static const uint8_t map[] = + { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni = ieee80211_ref_node(vap->iv_bss); + struct ieee80211_rateset *rs = &ni->ni_rates; + struct r92c_fw_cmd_macid_cfg cmd; + uint32_t rates, basicrates; + uint8_t mode; + int maxrate, maxbasicrate, error, i, j; + + /* Get normal and basic rates mask. */ + rates = basicrates = 0; + maxrate = maxbasicrate = 0; + for (i = 0; i < rs->rs_nrates; i++) { + /* Convert 802.11 rate to HW rate index. */ + for (j = 0; j < nitems(map); j++) + if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == map[j]) + break; + if (j == nitems(map)) /* Unknown rate, skip. */ + continue; + rates |= 1 << j; + if (j > maxrate) + maxrate = j; + if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) { + basicrates |= 1 << j; + if (j > maxbasicrate) + maxbasicrate = j; + } + } + if (ic->ic_curmode == IEEE80211_MODE_11B) + mode = R92C_RAID_11B; + else + mode = R92C_RAID_11BG; + DPRINTF(("mode=0x%x rates=0x%08x, basicrates=0x%08x\n", + mode, rates, basicrates)); + + /* Set rates mask for group addressed frames. */ + cmd.macid = RTWN_MACID_BC | RTWN_MACID_VALID; + cmd.mask = htole32(mode << 28 | basicrates); + error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); + if (error != 0) { + device_printf(sc->sc_dev, + "could not add broadcast station\n"); + return (error); + } + /* Set initial MRR rate. */ + DPRINTF(("maxbasicrate=%d\n", maxbasicrate)); + rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BC), + maxbasicrate); + + /* Set rates mask for unicast frames. */ + cmd.macid = RTWN_MACID_BSS | RTWN_MACID_VALID; + cmd.mask = htole32(mode << 28 | rates); + error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); + if (error != 0) { + device_printf(sc->sc_dev, "could not add BSS station\n"); + return (error); + } + /* Set initial MRR rate. */ + DPRINTF(("maxrate=%d\n", maxrate)); + rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BSS), + maxrate); + + /* Configure Automatic Rate Fallback Register. */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + if (rates & 0x0c) + rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0d)); + else + rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0f)); + } else + rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0ff5)); + + /* Indicate highest supported rate. */ + ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; + return (0); +} + +static void +rtwn_tsf_sync_enable(struct rtwn_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni = vap->iv_bss; + uint64_t tsf; + + /* Enable TSF synchronization. */ + rtwn_write_1(sc, R92C_BCN_CTRL, + rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0); + + rtwn_write_1(sc, R92C_BCN_CTRL, + rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN); + + /* Set initial TSF. */ + memcpy(&tsf, ni->ni_tstamp.data, 8); + tsf = le64toh(tsf); + tsf = tsf - (tsf % (vap->iv_bss->ni_intval * IEEE80211_DUR_TU)); + tsf -= IEEE80211_DUR_TU; + rtwn_write_4(sc, R92C_TSFTR + 0, tsf); + rtwn_write_4(sc, R92C_TSFTR + 4, tsf >> 32); + + rtwn_write_1(sc, R92C_BCN_CTRL, + rtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN); +} + +static void +rtwn_set_led(struct rtwn_softc *sc, int led, int on) +{ + uint8_t reg; + + if (led == RTWN_LED_LINK) { + reg = rtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; + if (!on) + reg |= R92C_LEDCFG2_DIS; + else + reg |= R92C_LEDCFG2_EN; + rtwn_write_1(sc, R92C_LEDCFG2, reg); + sc->ledlink = on; /* Save LED state. */ + } +} + +static void +rtwn_calib_to(void *arg) +{ + struct rtwn_softc *sc = arg; + struct r92c_fw_cmd_rssi cmd; + + if (sc->avg_pwdb != -1) { + /* Indicate Rx signal strength to FW for rate adaptation. */ + memset(&cmd, 0, sizeof(cmd)); + cmd.macid = 0; /* BSS. */ + cmd.pwdb = sc->avg_pwdb; + DPRINTFN(3, ("sending RSSI command avg=%d\n", sc->avg_pwdb)); + rtwn_fw_cmd(sc, R92C_CMD_RSSI_SETTING, &cmd, sizeof(cmd)); + } + + /* Do temperature compensation. */ + rtwn_temp_calib(sc); + + callout_reset(&sc->calib_to, hz * 2, rtwn_calib_to, sc); +} + +static int +rtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rtwn_vap *rvp = RTWN_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct rtwn_softc *sc = ic->ic_softc; + uint32_t reg; + + IEEE80211_UNLOCK(ic); + RTWN_LOCK(sc); + + if (vap->iv_state == IEEE80211_S_RUN) { + /* Stop calibration. */ + callout_stop(&sc->calib_to); + + /* Turn link LED off. */ + rtwn_set_led(sc, RTWN_LED_LINK, 0); + + /* Set media status to 'No Link'. */ + reg = rtwn_read_4(sc, R92C_CR); + reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_NOLINK); + rtwn_write_4(sc, R92C_CR, reg); + + /* Stop Rx of data frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP2, 0); + + /* Rest TSF. */ + rtwn_write_1(sc, R92C_DUAL_TSF_RST, 0x03); + + /* Disable TSF synchronization. */ + rtwn_write_1(sc, R92C_BCN_CTRL, + rtwn_read_1(sc, R92C_BCN_CTRL) | + R92C_BCN_CTRL_DIS_TSF_UDT0); + + /* Reset EDCA parameters. */ + rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217); + rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317); + rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320); + rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444); + } + switch (nstate) { + case IEEE80211_S_INIT: + /* Turn link LED off. */ + rtwn_set_led(sc, RTWN_LED_LINK, 0); + break; + case IEEE80211_S_SCAN: + if (vap->iv_state != IEEE80211_S_SCAN) { + /* Allow Rx from any BSSID. */ + rtwn_write_4(sc, R92C_RCR, + rtwn_read_4(sc, R92C_RCR) & + ~(R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN)); + + /* Set gain for scanning. */ + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); + reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20); + rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); + reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20); + rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); + } + + /* Make link LED blink during scan. */ + rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); + + /* Pause AC Tx queues. */ + rtwn_write_1(sc, R92C_TXPAUSE, + rtwn_read_1(sc, R92C_TXPAUSE) | 0x0f); + break; + case IEEE80211_S_AUTH: + /* Set initial gain under link. */ + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); + reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32); + rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); + reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32); + rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); + rtwn_set_chan(sc, ic->ic_curchan, NULL); + break; + case IEEE80211_S_RUN: + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* Enable Rx of data frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); + + /* Turn link LED on. */ + rtwn_set_led(sc, RTWN_LED_LINK, 1); + break; + } + + /* Set media status to 'Associated'. */ + reg = rtwn_read_4(sc, R92C_CR); + reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA); + rtwn_write_4(sc, R92C_CR, reg); + + /* Set BSSID. */ + rtwn_write_4(sc, R92C_BSSID + 0, LE_READ_4(&ni->ni_bssid[0])); + rtwn_write_4(sc, R92C_BSSID + 4, LE_READ_2(&ni->ni_bssid[4])); + + if (ic->ic_curmode == IEEE80211_MODE_11B) + rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 0); + else /* 802.11b/g */ + rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 3); + + /* Enable Rx of data frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); + + /* Flush all AC queues. */ + rtwn_write_1(sc, R92C_TXPAUSE, 0); + + /* Set beacon interval. */ + rtwn_write_2(sc, R92C_BCN_INTERVAL, ni->ni_intval); + + /* Allow Rx from our BSSID only. */ + rtwn_write_4(sc, R92C_RCR, + rtwn_read_4(sc, R92C_RCR) | + R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN); + + /* Enable TSF synchronization. */ + rtwn_tsf_sync_enable(sc); + + rtwn_write_1(sc, R92C_SIFS_CCK + 1, 10); + rtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10); + rtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10); + rtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10); + rtwn_write_1(sc, R92C_R2T_SIFS + 1, 10); + rtwn_write_1(sc, R92C_T2T_SIFS + 1, 10); + + /* Intialize rate adaptation. */ + rtwn_ra_init(sc); + /* Turn link LED on. */ + rtwn_set_led(sc, RTWN_LED_LINK, 1); + + sc->avg_pwdb = -1; /* Reset average RSSI. */ + /* Reset temperature calibration state machine. */ + sc->thcal_state = 0; + sc->thcal_lctemp = 0; + /* Start periodic calibration. */ + callout_reset(&sc->calib_to, hz * 2, rtwn_calib_to, sc); + break; + default: + break; + } + RTWN_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (rvp->newstate(vap, nstate, arg)); +} + +static int +rtwn_updateedca(struct ieee80211com *ic) +{ + struct rtwn_softc *sc = ic->ic_softc; + const uint16_t aci2reg[WME_NUM_AC] = { + R92C_EDCA_BE_PARAM, + R92C_EDCA_BK_PARAM, + R92C_EDCA_VI_PARAM, + R92C_EDCA_VO_PARAM + }; + int aci, aifs, slottime; + + IEEE80211_LOCK(ic); + slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + for (aci = 0; aci < WME_NUM_AC; aci++) { + const struct wmeParams *ac = + &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; + /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ + aifs = ac->wmep_aifsn * slottime + 10; + rtwn_write_4(sc, aci2reg[aci], + SM(R92C_EDCA_PARAM_TXOP, ac->wmep_txopLimit) | + SM(R92C_EDCA_PARAM_ECWMIN, ac->wmep_logcwmin) | + SM(R92C_EDCA_PARAM_ECWMAX, ac->wmep_logcwmax) | + SM(R92C_EDCA_PARAM_AIFS, aifs)); + } + IEEE80211_UNLOCK(ic); + return (0); +} + +static void +rtwn_update_avgrssi(struct rtwn_softc *sc, int rate, int8_t rssi) +{ + int pwdb; + + /* Convert antenna signal to percentage. */ + if (rssi <= -100 || rssi >= 20) + pwdb = 0; + else if (rssi >= 0) + pwdb = 100; + else + pwdb = 100 + rssi; + if (rate <= 3) { + /* CCK gain is smaller than OFDM/MCS gain. */ + pwdb += 6; + if (pwdb > 100) + pwdb = 100; + if (pwdb <= 14) + pwdb -= 4; + else if (pwdb <= 26) + pwdb -= 8; + else if (pwdb <= 34) + pwdb -= 6; + else if (pwdb <= 42) + pwdb -= 2; + } + if (sc->avg_pwdb == -1) /* Init. */ + sc->avg_pwdb = pwdb; + else if (sc->avg_pwdb < pwdb) + sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1; + else + sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20); + DPRINTFN(4, ("PWDB=%d EMA=%d\n", pwdb, sc->avg_pwdb)); +} + +static int8_t +rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt) +{ + static const int8_t cckoff[] = { 16, -12, -26, -46 }; + struct r92c_rx_phystat *phy; + struct r92c_rx_cck *cck; + uint8_t rpt; + int8_t rssi; + + if (rate <= 3) { + cck = (struct r92c_rx_cck *)physt; + if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) { + rpt = (cck->agc_rpt >> 5) & 0x3; + rssi = (cck->agc_rpt & 0x1f) << 1; + } else { + rpt = (cck->agc_rpt >> 6) & 0x3; + rssi = cck->agc_rpt & 0x3e; + } + rssi = cckoff[rpt] - rssi; + } else { /* OFDM/HT. */ + phy = (struct r92c_rx_phystat *)physt; + rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 110; + } + return (rssi); +} + +static void +rtwn_rx_frame(struct rtwn_softc *sc, struct r92c_rx_desc *rx_desc, + struct rtwn_rx_data *rx_data, int desc_idx) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct r92c_rx_phystat *phy = NULL; + uint32_t rxdw0, rxdw3; + struct mbuf *m, *m1; + bus_dma_segment_t segs[1]; + bus_addr_t physaddr; + uint8_t rate; + int8_t rssi = 0, nf; + int infosz, nsegs, pktlen, shift, error; + + rxdw0 = le32toh(rx_desc->rxdw0); + rxdw3 = le32toh(rx_desc->rxdw3); + + if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { + /* + * This should not happen since we setup our Rx filter + * to not receive these frames. + */ + counter_u64_add(ic->ic_ierrors, 1); + return; + } + + pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); + if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) { + counter_u64_add(ic->ic_ierrors, 1); + return; + } + + rate = MS(rxdw3, R92C_RXDW3_RATE); + infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; + if (infosz > sizeof(struct r92c_rx_phystat)) + infosz = sizeof(struct r92c_rx_phystat); + shift = MS(rxdw0, R92C_RXDW0_SHIFT); + + /* Get RSSI from PHY status descriptor if present. */ + if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { + phy = mtod(rx_data->m, struct r92c_rx_phystat *); + rssi = rtwn_get_rssi(sc, rate, phy); + /* Update our average RSSI. */ + rtwn_update_avgrssi(sc, rate, rssi); + } + + DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d shift=%d rssi=%d\n", + pktlen, rate, infosz, shift, rssi)); + + m1 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m1 == NULL) { + counter_u64_add(ic->ic_ierrors, 1); + return; + } + bus_dmamap_unload(sc->rx_ring.data_dmat, rx_data->map); + + error = bus_dmamap_load(sc->rx_ring.data_dmat, rx_data->map, + mtod(m1, void *), MCLBYTES, rtwn_dma_map_addr, + &physaddr, 0); + if (error != 0) { + m_freem(m1); + + if (bus_dmamap_load_mbuf_sg(sc->rx_ring.data_dmat, + rx_data->map, rx_data->m, segs, &nsegs, 0)) + panic("%s: could not load old RX mbuf", + device_get_name(sc->sc_dev)); + + /* Physical address may have changed. */ + rtwn_setup_rx_desc(sc, rx_desc, physaddr, MCLBYTES, desc_idx); + counter_u64_add(ic->ic_ierrors, 1); + return; + } + + /* Finalize mbuf. */ + m = rx_data->m; + rx_data->m = m1; + m->m_pkthdr.len = m->m_len = pktlen + infosz + shift; + + /* Update RX descriptor. */ + rtwn_setup_rx_desc(sc, rx_desc, physaddr, MCLBYTES, desc_idx); + + /* Get ieee80211 frame header. */ + if (rxdw0 & R92C_RXDW0_PHYST) + m_adj(m, infosz + shift); + else + m_adj(m, shift); + + nf = -95; + if (ieee80211_radiotap_active(ic)) { + struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + if (!(rxdw3 & R92C_RXDW3_HT)) { + switch (rate) { + /* CCK. */ + case 0: tap->wr_rate = 2; break; + case 1: tap->wr_rate = 4; break; + case 2: tap->wr_rate = 11; break; + case 3: tap->wr_rate = 22; break; + /* OFDM. */ + case 4: tap->wr_rate = 12; break; + case 5: tap->wr_rate = 18; break; + case 6: tap->wr_rate = 24; break; + case 7: tap->wr_rate = 36; break; + case 8: tap->wr_rate = 48; break; + case 9: tap->wr_rate = 72; break; + case 10: tap->wr_rate = 96; break; + case 11: tap->wr_rate = 108; break; + } + } else if (rate >= 12) { /* MCS0~15. */ + /* Bit 7 set means HT MCS instead of rate. */ + tap->wr_rate = 0x80 | (rate - 12); + } + tap->wr_dbm_antsignal = rssi; + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + } + + RTWN_UNLOCK(sc); + wh = mtod(m, struct ieee80211_frame *); + + /* Send the frame to the 802.11 layer. */ + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi - nf, nf); + /* Node is no longer needed. */ + ieee80211_free_node(ni); + } else + (void)ieee80211_input_all(ic, m, rssi - nf, nf); + + RTWN_LOCK(sc); +} + +static int +rtwn_tx(struct rtwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + struct rtwn_tx_ring *tx_ring; + struct rtwn_tx_data *data; + struct r92c_tx_desc *txd; + bus_dma_segment_t segs[1]; + uint16_t qos; + uint8_t raid, type, tid, qid; + int nsegs, error; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* Encrypt the frame if need be. */ + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + return (ENOBUFS); + } + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + } + + if (IEEE80211_QOS_HAS_SEQ(wh)) { + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + } else { + qos = 0; + tid = 0; + } + + switch (type) { + case IEEE80211_FC0_TYPE_CTL: + case IEEE80211_FC0_TYPE_MGT: + qid = RTWN_VO_QUEUE; + break; + default: + qid = M_WME_GETAC(m); + break; + } + + /* Grab a Tx buffer from the ring. */ + tx_ring = &sc->tx_ring[qid]; + data = &tx_ring->tx_data[tx_ring->cur]; + if (data->m != NULL) { + m_freem(m); + return (ENOBUFS); + } + + /* Fill Tx descriptor. */ + txd = &tx_ring->desc[tx_ring->cur]; + if (htole32(txd->txdw0) & R92C_RXDW0_OWN) { + m_freem(m); + return (ENOBUFS); + } + txd->txdw0 = htole32( + SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | + SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | + R92C_TXDW0_FSG | R92C_TXDW0_LSG); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); + + txd->txdw1 = 0; + txd->txdw4 = 0; + txd->txdw5 = 0; + + /* XXX TODO: rate control; implement low-rate for EAPOL */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + type == IEEE80211_FC0_TYPE_DATA) { + if (ic->ic_curmode == IEEE80211_MODE_11B) + raid = R92C_RAID_11B; + else + raid = R92C_RAID_11BG; + txd->txdw1 |= htole32( + SM(R92C_TXDW1_MACID, RTWN_MACID_BSS) | + SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) | + SM(R92C_TXDW1_RAID, raid) | + R92C_TXDW1_AGGBK); + + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { + txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF | + R92C_TXDW4_HWRTSEN); + } else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { + txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | + R92C_TXDW4_HWRTSEN); + } + } + + /* XXX TODO: implement rate control */ + + /* Send RTS at OFDM24. */ + txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8)); + txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FBLIMIT, 0xf)); + /* Send data at OFDM54. */ + txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11)); + txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FBLIMIT, 0x1f)); + + } else { + txd->txdw1 |= htole32( + SM(R92C_TXDW1_MACID, 0) | + SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) | + SM(R92C_TXDW1_RAID, R92C_RAID_11B)); + + /* Force CCK1. */ + txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); + txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0)); + } + /* Set sequence number (already little endian). */ + txd->txdseq = *(uint16_t *)wh->i_seq; + + if (!qos) { + /* Use HW sequence numbering for non-QoS frames. */ + txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ); + txd->txdseq |= htole16(0x8000); + } else + txd->txdw4 |= htole32(R92C_TXDW4_QOS); + + error = bus_dmamap_load_mbuf_sg(tx_ring->data_dmat, data->map, m, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); + m_freem(m); + return (error); + } + if (error != 0) { + struct mbuf *mnew; + + mnew = m_defrag(m, M_NOWAIT); + if (mnew == NULL) { + device_printf(sc->sc_dev, + "can't defragment mbuf\n"); + m_freem(m); + return (ENOBUFS); + } + m = mnew; + + error = bus_dmamap_load_mbuf_sg(tx_ring->data_dmat, data->map, + m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "can't map mbuf (error %d)\n", error); + m_freem(m); + return (error); + } + } + + txd->txbufaddr = htole32(segs[0].ds_addr); + txd->txbufsize = htole16(m->m_pkthdr.len); + bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize, + BUS_SPACE_BARRIER_WRITE); + txd->txdw0 |= htole32(R92C_TXDW0_OWN); + + bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_sync(tx_ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); + + data->m = m; + data->ni = ni; + + if (ieee80211_radiotap_active_vap(vap)) { + struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); + + ieee80211_radiotap_tx(vap, m); + } + + tx_ring->cur = (tx_ring->cur + 1) % RTWN_TX_LIST_COUNT; + tx_ring->queued++; + + if (tx_ring->queued >= (RTWN_TX_LIST_COUNT - 1)) + sc->qfullmsk |= (1 << qid); + + /* Kick TX. */ + rtwn_write_2(sc, R92C_PCIE_CTRL_REG, (1 << qid)); + return (0); +} + +static void +rtwn_tx_done(struct rtwn_softc *sc, int qid) +{ + struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; + struct rtwn_tx_data *tx_data; + struct r92c_tx_desc *tx_desc; + int i; + + bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, + BUS_DMASYNC_POSTREAD); + + for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { + tx_data = &tx_ring->tx_data[i]; + if (tx_data->m == NULL) + continue; + + tx_desc = &tx_ring->desc[i]; + if (le32toh(tx_desc->txdw0) & R92C_TXDW0_OWN) + continue; + + bus_dmamap_unload(tx_ring->desc_dmat, tx_ring->desc_map); + + /* + * XXX TODO: figure out whether the transmit succeeded or not. + * .. and then notify rate control. + */ + ieee80211_tx_complete(tx_data->ni, tx_data->m, 0); + tx_data->ni = NULL; + tx_data->m = NULL; + + sc->sc_tx_timer = 0; + tx_ring->queued--; + } + + if (tx_ring->queued < (RTWN_TX_LIST_COUNT - 1)) + sc->qfullmsk &= ~(1 << qid); + rtwn_start(sc); +} + +static int +rtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct rtwn_softc *sc = ic->ic_softc; + + RTWN_LOCK(sc); + + /* Prevent management frames from being sent if we're not ready. */ + if (!(sc->sc_flags & RTWN_RUNNING)) { + RTWN_UNLOCK(sc); + m_freem(m); + return (ENETDOWN); + } + + if (rtwn_tx(sc, m, ni) != 0) { + m_freem(m); + RTWN_UNLOCK(sc); + return (EIO); + } + sc->sc_tx_timer = 5; + RTWN_UNLOCK(sc); + return (0); +} + +static int +rtwn_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct rtwn_softc *sc = ic->ic_softc; + int error; + + RTWN_LOCK(sc); + if ((sc->sc_flags & RTWN_RUNNING) == 0) { + RTWN_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RTWN_UNLOCK(sc); + return (error); + } + rtwn_start(sc); + RTWN_UNLOCK(sc); + return (0); +} + +static void +rtwn_parent(struct ieee80211com *ic) +{ + struct rtwn_softc *sc = ic->ic_softc; + int startall = 0; + + RTWN_LOCK(sc); + if (ic->ic_nrunning> 0) { + if (!(sc->sc_flags & RTWN_RUNNING)) { + rtwn_init_locked(sc); + startall = 1; + } + } else if (sc->sc_flags & RTWN_RUNNING) + rtwn_stop_locked(sc); + RTWN_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +rtwn_start(struct rtwn_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RTWN_LOCK_ASSERT(sc); + + if ((sc->sc_flags & RTWN_RUNNING) == 0) + return; + + while (sc->qfullmsk == 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (rtwn_tx(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + continue; + } + sc->sc_tx_timer = 5; + } +} + +static void +rtwn_watchdog(void *arg) +{ + struct rtwn_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + RTWN_LOCK_ASSERT(sc); + + KASSERT(sc->sc_flags & RTWN_RUNNING, ("not running")); + + if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { + ic_printf(ic, "device timeout\n"); + ieee80211_runtask(ic, &sc->sc_reinit_task); + return; + } + callout_reset(&sc->watchdog_to, hz, rtwn_watchdog, sc); +} + +static int +rtwn_power_on(struct rtwn_softc *sc) +{ + uint32_t reg; + int ntries; + + /* Wait for autoload done bit. */ + for (ntries = 0; ntries < 1000; ntries++) { + if (rtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN) + break; + DELAY(5); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for chip autoload\n"); + return (ETIMEDOUT); + } + + /* Unlock ISO/CLK/Power control register. */ + rtwn_write_1(sc, R92C_RSV_CTRL, 0); + + /* TODO: check if we need this for 8188CE */ + if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { + /* bt coex */ + reg = rtwn_read_4(sc, R92C_APS_FSMCO); + reg |= (R92C_APS_FSMCO_SOP_ABG | + R92C_APS_FSMCO_SOP_AMB | + R92C_APS_FSMCO_XOP_BTCK); + rtwn_write_4(sc, R92C_APS_FSMCO, reg); + } + + /* Move SPS into PWM mode. */ + rtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b); + + /* Set low byte to 0x0f, leave others unchanged. */ + rtwn_write_4(sc, R92C_AFE_XTAL_CTRL, + (rtwn_read_4(sc, R92C_AFE_XTAL_CTRL) & 0xffffff00) | 0x0f); + + /* TODO: check if we need this for 8188CE */ + if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { + /* bt coex */ + reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL); + reg &= (~0x00024800); /* XXX magic from linux */ + rtwn_write_4(sc, R92C_AFE_XTAL_CTRL, reg); + } + + rtwn_write_2(sc, R92C_SYS_ISO_CTRL, + (rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & 0xff) | + R92C_SYS_ISO_CTRL_PWC_EV12V | R92C_SYS_ISO_CTRL_DIOR); + DELAY(200); + + /* TODO: linux does additional btcoex stuff here */ + + /* Auto enable WLAN. */ + rtwn_write_2(sc, R92C_APS_FSMCO, + rtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); + for (ntries = 0; ntries < 1000; ntries++) { + if (!(rtwn_read_2(sc, R92C_APS_FSMCO) & + R92C_APS_FSMCO_APFM_ONMAC)) + break; + DELAY(5); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, "timeout waiting for MAC auto ON\n"); + return (ETIMEDOUT); + } + + /* Enable radio, GPIO and LED functions. */ + rtwn_write_2(sc, R92C_APS_FSMCO, + R92C_APS_FSMCO_AFSM_PCIE | + R92C_APS_FSMCO_PDN_EN | + R92C_APS_FSMCO_PFM_ALDN); + /* Release RF digital isolation. */ + rtwn_write_2(sc, R92C_SYS_ISO_CTRL, + rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR); + + if (sc->chip & RTWN_CHIP_92C) + rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x77); + else + rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x22); + + rtwn_write_4(sc, R92C_INT_MIG, 0); + + if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { + /* bt coex */ + reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL + 2); + reg &= 0xfd; /* XXX magic from linux */ + rtwn_write_4(sc, R92C_AFE_XTAL_CTRL + 2, reg); + } + + rtwn_write_1(sc, R92C_GPIO_MUXCFG, + rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_RFKILL); + + reg = rtwn_read_1(sc, R92C_GPIO_IO_SEL); + if (!(reg & R92C_GPIO_IO_SEL_RFKILL)) { + device_printf(sc->sc_dev, + "radio is disabled by hardware switch\n"); + return (EPERM); + } + + /* Initialize MAC. */ + reg = rtwn_read_1(sc, R92C_APSD_CTRL); + rtwn_write_1(sc, R92C_APSD_CTRL, + rtwn_read_1(sc, R92C_APSD_CTRL) & ~R92C_APSD_CTRL_OFF); + for (ntries = 0; ntries < 200; ntries++) { + if (!(rtwn_read_1(sc, R92C_APSD_CTRL) & + R92C_APSD_CTRL_OFF_STATUS)) + break; + DELAY(500); + } + if (ntries == 200) { + device_printf(sc->sc_dev, + "timeout waiting for MAC initialization\n"); + return (ETIMEDOUT); + } + + /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ + reg = rtwn_read_2(sc, R92C_CR); + reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | + R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | + R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | + R92C_CR_ENSEC; + rtwn_write_2(sc, R92C_CR, reg); + + rtwn_write_1(sc, 0xfe10, 0x19); + + return (0); +} + +static int +rtwn_llt_init(struct rtwn_softc *sc) +{ + int i, error; + + /* Reserve pages [0; R92C_TX_PAGE_COUNT]. */ + for (i = 0; i < R92C_TX_PAGE_COUNT; i++) { + if ((error = rtwn_llt_write(sc, i, i + 1)) != 0) + return (error); + } + /* NB: 0xff indicates end-of-list. */ + if ((error = rtwn_llt_write(sc, i, 0xff)) != 0) + return (error); + /* + * Use pages [R92C_TX_PAGE_COUNT + 1; R92C_TXPKTBUF_COUNT - 1] + * as ring buffer. + */ + for (++i; i < R92C_TXPKTBUF_COUNT - 1; i++) { + if ((error = rtwn_llt_write(sc, i, i + 1)) != 0) + return (error); + } + /* Make the last page point to the beginning of the ring buffer. */ + error = rtwn_llt_write(sc, i, R92C_TX_PAGE_COUNT + 1); + return (error); +} + +static void +rtwn_fw_reset(struct rtwn_softc *sc) +{ + uint16_t reg; + int ntries; + + /* Tell 8051 to reset itself. */ + rtwn_write_1(sc, R92C_HMETFR + 3, 0x20); + + /* Wait until 8051 resets by itself. */ + for (ntries = 0; ntries < 100; ntries++) { + reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); + if (!(reg & R92C_SYS_FUNC_EN_CPUEN)) + goto sleep; + DELAY(50); + } + /* Force 8051 reset. */ + rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); +sleep: + /* + * We must sleep for one second to let the firmware settle. + * Accessing registers too early will hang the whole system. + */ + if (msleep(®, &sc->sc_mtx, 0, "rtwnrst", hz)) { + device_printf(sc->sc_dev, "timeout waiting for firmware " + "initialization to complete\n"); + } +} + +static void +rtwn_fw_loadpage(struct rtwn_softc *sc, int page, const uint8_t *buf, int len) +{ + uint32_t reg; + int off, mlen, i; + + reg = rtwn_read_4(sc, R92C_MCUFWDL); + reg = RW(reg, R92C_MCUFWDL_PAGE, page); + rtwn_write_4(sc, R92C_MCUFWDL, reg); + + DELAY(5); + + off = R92C_FW_START_ADDR; + while (len > 0) { + if (len > 196) + mlen = 196; + else if (len > 4) + mlen = 4; + else + mlen = 1; + for (i = 0; i < mlen; i++) + rtwn_write_1(sc, off++, buf[i]); + buf += mlen; + len -= mlen; + } +} + +static int +rtwn_load_firmware(struct rtwn_softc *sc) +{ + const struct firmware *fw; + const struct r92c_fw_hdr *hdr; + const char *name; + const u_char *ptr; + size_t len; + uint32_t reg; + int mlen, ntries, page, error = 0; + + /* Read firmware image from the filesystem. */ + if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == + RTWN_CHIP_UMC_A_CUT) + name = "rtwn-rtl8192cfwU"; + else + name = "rtwn-rtl8192cfwU_B"; + RTWN_UNLOCK(sc); + fw = firmware_get(name); + RTWN_LOCK(sc); + if (fw == NULL) { + device_printf(sc->sc_dev, + "could not read firmware %s\n", name); + return (ENOENT); + } + len = fw->datasize; + if (len < sizeof(*hdr)) { + device_printf(sc->sc_dev, "firmware too short\n"); + error = EINVAL; + goto fail; + } + ptr = fw->data; + hdr = (const struct r92c_fw_hdr *)ptr; + /* Check if there is a valid FW header and skip it. */ + if ((le16toh(hdr->signature) >> 4) == 0x88c || + (le16toh(hdr->signature) >> 4) == 0x92c) { + DPRINTF(("FW V%d.%d %02d-%02d %02d:%02d\n", + le16toh(hdr->version), le16toh(hdr->subversion), + hdr->month, hdr->date, hdr->hour, hdr->minute)); + ptr += sizeof(*hdr); + len -= sizeof(*hdr); + } + + if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) + rtwn_fw_reset(sc); + + /* Enable FW download. */ + rtwn_write_2(sc, R92C_SYS_FUNC_EN, + rtwn_read_2(sc, R92C_SYS_FUNC_EN) | + R92C_SYS_FUNC_EN_CPUEN); + rtwn_write_1(sc, R92C_MCUFWDL, + rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN); + rtwn_write_1(sc, R92C_MCUFWDL + 2, + rtwn_read_1(sc, R92C_MCUFWDL + 2) & ~0x08); + + /* Reset the FWDL checksum. */ + rtwn_write_1(sc, R92C_MCUFWDL, + rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT); + + for (page = 0; len > 0; page++) { + mlen = MIN(len, R92C_FW_PAGE_SIZE); + rtwn_fw_loadpage(sc, page, ptr, mlen); + ptr += mlen; + len -= mlen; + } + + /* Disable FW download. */ + rtwn_write_1(sc, R92C_MCUFWDL, + rtwn_read_1(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_EN); + rtwn_write_1(sc, R92C_MCUFWDL + 1, 0); + + /* Wait for checksum report. */ + for (ntries = 0; ntries < 1000; ntries++) { + if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT) + break; + DELAY(5); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for checksum report\n"); + error = ETIMEDOUT; + goto fail; + } + + reg = rtwn_read_4(sc, R92C_MCUFWDL); + reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY; + rtwn_write_4(sc, R92C_MCUFWDL, reg); + /* Wait for firmware readiness. */ + for (ntries = 0; ntries < 2000; ntries++) { + if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY) + break; + DELAY(50); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for firmware readiness\n"); + error = ETIMEDOUT; + goto fail; + } +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + +static int +rtwn_dma_init(struct rtwn_softc *sc) +{ + uint32_t reg; + int error; + + /* Initialize LLT table. */ + error = rtwn_llt_init(sc); + if (error != 0) + return error; + + /* Set number of pages for normal priority queue. */ + rtwn_write_2(sc, R92C_RQPN_NPQ, 0); + rtwn_write_4(sc, R92C_RQPN, + /* Set number of pages for public queue. */ + SM(R92C_RQPN_PUBQ, R92C_PUBQ_NPAGES) | + /* Set number of pages for high priority queue. */ + SM(R92C_RQPN_HPQ, R92C_HPQ_NPAGES) | + /* Set number of pages for low priority queue. */ + SM(R92C_RQPN_LPQ, R92C_LPQ_NPAGES) | + /* Load values. */ + R92C_RQPN_LD); + + rtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, R92C_TX_PAGE_BOUNDARY); + rtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, R92C_TX_PAGE_BOUNDARY); + rtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, R92C_TX_PAGE_BOUNDARY); + rtwn_write_1(sc, R92C_TRXFF_BNDY, R92C_TX_PAGE_BOUNDARY); + rtwn_write_1(sc, R92C_TDECTRL + 1, R92C_TX_PAGE_BOUNDARY); + + reg = rtwn_read_2(sc, R92C_TRXDMA_CTRL); + reg &= ~R92C_TRXDMA_CTRL_QMAP_M; + reg |= 0xF771; + rtwn_write_2(sc, R92C_TRXDMA_CTRL, reg); + + rtwn_write_4(sc, R92C_TCR, R92C_TCR_CFENDFORM | (1 << 12) | (1 << 13)); + + /* Configure Tx DMA. */ + rtwn_write_4(sc, R92C_BKQ_DESA, sc->tx_ring[RTWN_BK_QUEUE].paddr); + rtwn_write_4(sc, R92C_BEQ_DESA, sc->tx_ring[RTWN_BE_QUEUE].paddr); + rtwn_write_4(sc, R92C_VIQ_DESA, sc->tx_ring[RTWN_VI_QUEUE].paddr); + rtwn_write_4(sc, R92C_VOQ_DESA, sc->tx_ring[RTWN_VO_QUEUE].paddr); + rtwn_write_4(sc, R92C_BCNQ_DESA, sc->tx_ring[RTWN_BEACON_QUEUE].paddr); + rtwn_write_4(sc, R92C_MGQ_DESA, sc->tx_ring[RTWN_MGNT_QUEUE].paddr); + rtwn_write_4(sc, R92C_HQ_DESA, sc->tx_ring[RTWN_HIGH_QUEUE].paddr); + + /* Configure Rx DMA. */ + rtwn_write_4(sc, R92C_RX_DESA, sc->rx_ring.paddr); + + /* Set Tx/Rx transfer page boundary. */ + rtwn_write_2(sc, R92C_TRXFF_BNDY + 2, 0x27ff); + + /* Set Tx/Rx transfer page size. */ + rtwn_write_1(sc, R92C_PBP, + SM(R92C_PBP_PSRX, R92C_PBP_128) | + SM(R92C_PBP_PSTX, R92C_PBP_128)); + return (0); +} + +static void +rtwn_mac_init(struct rtwn_softc *sc) +{ + int i; + + /* Write MAC initialization values. */ + for (i = 0; i < nitems(rtl8192ce_mac); i++) + rtwn_write_1(sc, rtl8192ce_mac[i].reg, rtl8192ce_mac[i].val); +} + +static void +rtwn_bb_init(struct rtwn_softc *sc) +{ + const struct rtwn_bb_prog *prog; + uint32_t reg; + int i; + + /* Enable BB and RF. */ + rtwn_write_2(sc, R92C_SYS_FUNC_EN, + rtwn_read_2(sc, R92C_SYS_FUNC_EN) | + R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST | + R92C_SYS_FUNC_EN_DIO_RF); + + rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83); + + rtwn_write_1(sc, R92C_RF_CTRL, + R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB); + + rtwn_write_1(sc, R92C_SYS_FUNC_EN, + R92C_SYS_FUNC_EN_DIO_PCIE | R92C_SYS_FUNC_EN_PCIEA | + R92C_SYS_FUNC_EN_PPLL | R92C_SYS_FUNC_EN_BB_GLB_RST | + R92C_SYS_FUNC_EN_BBRSTB); + + rtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80); + + rtwn_write_4(sc, R92C_LEDCFG0, + rtwn_read_4(sc, R92C_LEDCFG0) | 0x00800000); + + /* Select BB programming. */ + prog = (sc->chip & RTWN_CHIP_92C) ? + &rtl8192ce_bb_prog_2t : &rtl8192ce_bb_prog_1t; + + /* Write BB initialization values. */ + for (i = 0; i < prog->count; i++) { + rtwn_bb_write(sc, prog->regs[i], prog->vals[i]); + DELAY(1); + } + + if (sc->chip & RTWN_CHIP_92C_1T2R) { + /* 8192C 1T only configuration. */ + reg = rtwn_bb_read(sc, R92C_FPGA0_TXINFO); + reg = (reg & ~0x00000003) | 0x2; + rtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg); + + reg = rtwn_bb_read(sc, R92C_FPGA1_TXINFO); + reg = (reg & ~0x00300033) | 0x00200022; + rtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg); + + reg = rtwn_bb_read(sc, R92C_CCK0_AFESETTING); + reg = (reg & ~0xff000000) | 0x45 << 24; + rtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); + reg = (reg & ~0x000000ff) | 0x23; + rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1); + reg = (reg & ~0x00000030) | 1 << 4; + rtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg); + + reg = rtwn_bb_read(sc, 0xe74); + reg = (reg & ~0x0c000000) | 2 << 26; + rtwn_bb_write(sc, 0xe74, reg); + reg = rtwn_bb_read(sc, 0xe78); + reg = (reg & ~0x0c000000) | 2 << 26; + rtwn_bb_write(sc, 0xe78, reg); + reg = rtwn_bb_read(sc, 0xe7c); + reg = (reg & ~0x0c000000) | 2 << 26; + rtwn_bb_write(sc, 0xe7c, reg); + reg = rtwn_bb_read(sc, 0xe80); + reg = (reg & ~0x0c000000) | 2 << 26; + rtwn_bb_write(sc, 0xe80, reg); + reg = rtwn_bb_read(sc, 0xe88); + reg = (reg & ~0x0c000000) | 2 << 26; + rtwn_bb_write(sc, 0xe88, reg); + } + + /* Write AGC values. */ + for (i = 0; i < prog->agccount; i++) { + rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, + prog->agcvals[i]); + DELAY(1); + } + + if (rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) & + R92C_HSSI_PARAM2_CCK_HIPWR) + sc->sc_flags |= RTWN_FLAG_CCK_HIPWR; +} + +static void +rtwn_rf_init(struct rtwn_softc *sc) +{ + const struct rtwn_rf_prog *prog; + uint32_t reg, type; + int i, j, idx, off; + + /* Select RF programming based on board type. */ + if (!(sc->chip & RTWN_CHIP_92C)) { + if (sc->board_type == R92C_BOARD_TYPE_MINICARD) + prog = rtl8188ce_rf_prog; + else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) + prog = rtl8188ru_rf_prog; + else + prog = rtl8188cu_rf_prog; + } else + prog = rtl8192ce_rf_prog; + + for (i = 0; i < sc->nrxchains; i++) { + /* Save RF_ENV control type. */ + idx = i / 2; + off = (i % 2) * 16; + reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); + type = (reg >> off) & 0x10; + + /* Set RF_ENV enable. */ + reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); + reg |= 0x100000; + rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); + DELAY(1); + /* Set RF_ENV output high. */ + reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); + reg |= 0x10; + rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); + DELAY(1); + /* Set address and data lengths of RF registers. */ + reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); + reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH; + rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); + DELAY(1); + reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); + reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH; + rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); + DELAY(1); + + /* Write RF initialization values for this chain. */ + for (j = 0; j < prog[i].count; j++) { + if (prog[i].regs[j] >= 0xf9 && + prog[i].regs[j] <= 0xfe) { + /* + * These are fake RF registers offsets that + * indicate a delay is required. + */ + DELAY(50); + continue; + } + rtwn_rf_write(sc, i, prog[i].regs[j], + prog[i].vals[j]); + DELAY(1); + } + + /* Restore RF_ENV control type. */ + reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); + reg &= ~(0x10 << off) | (type << off); + rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(idx), reg); + + /* Cache RF register CHNLBW. */ + sc->rf_chnlbw[i] = rtwn_rf_read(sc, i, R92C_RF_CHNLBW); + } + + if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == + RTWN_CHIP_UMC_A_CUT) { + rtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255); + rtwn_rf_write(sc, 0, R92C_RF_RX_G2, 0x50a00); + } +} + +static void +rtwn_cam_init(struct rtwn_softc *sc) +{ + /* Invalidate all CAM entries. */ + rtwn_write_4(sc, R92C_CAMCMD, + R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR); +} + +static void +rtwn_pa_bias_init(struct rtwn_softc *sc) +{ + uint8_t reg; + int i; + + for (i = 0; i < sc->nrxchains; i++) { + if (sc->pa_setting & (1 << i)) + continue; + rtwn_rf_write(sc, i, R92C_RF_IPA, 0x0f406); + rtwn_rf_write(sc, i, R92C_RF_IPA, 0x4f406); + rtwn_rf_write(sc, i, R92C_RF_IPA, 0x8f406); + rtwn_rf_write(sc, i, R92C_RF_IPA, 0xcf406); + } + if (!(sc->pa_setting & 0x10)) { + reg = rtwn_read_1(sc, 0x16); + reg = (reg & ~0xf0) | 0x90; + rtwn_write_1(sc, 0x16, reg); + } +} + +static void +rtwn_rxfilter_init(struct rtwn_softc *sc) +{ + /* Initialize Rx filter. */ + /* TODO: use better filter for monitor mode. */ + rtwn_write_4(sc, R92C_RCR, + R92C_RCR_AAP | R92C_RCR_APM | R92C_RCR_AM | R92C_RCR_AB | + R92C_RCR_APP_ICV | R92C_RCR_AMF | R92C_RCR_HTC_LOC_CTRL | + R92C_RCR_APP_MIC | R92C_RCR_APP_PHYSTS); + /* Accept all multicast frames. */ + rtwn_write_4(sc, R92C_MAR + 0, 0xffffffff); + rtwn_write_4(sc, R92C_MAR + 4, 0xffffffff); + /* Accept all management frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP0, 0xffff); + /* Reject all control frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); + /* Accept all data frames. */ + rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); +} + +static void +rtwn_edca_init(struct rtwn_softc *sc) +{ + + rtwn_write_2(sc, R92C_SPEC_SIFS, 0x1010); + rtwn_write_2(sc, R92C_MAC_SPEC_SIFS, 0x1010); + rtwn_write_2(sc, R92C_SIFS_CCK, 0x1010); + rtwn_write_2(sc, R92C_SIFS_OFDM, 0x0e0e); + rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x005ea42b); + rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a44f); + rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4322); + rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3222); +} + +static void +rtwn_write_txpower(struct rtwn_softc *sc, int chain, + uint16_t power[RTWN_RIDX_COUNT]) +{ + uint32_t reg; + + /* Write per-CCK rate Tx power. */ + if (chain == 0) { + reg = rtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32); + reg = RW(reg, R92C_TXAGC_A_CCK1, power[0]); + rtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg); + reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); + reg = RW(reg, R92C_TXAGC_A_CCK2, power[1]); + reg = RW(reg, R92C_TXAGC_A_CCK55, power[2]); + reg = RW(reg, R92C_TXAGC_A_CCK11, power[3]); + rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); + } else { + reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32); + reg = RW(reg, R92C_TXAGC_B_CCK1, power[0]); + reg = RW(reg, R92C_TXAGC_B_CCK2, power[1]); + reg = RW(reg, R92C_TXAGC_B_CCK55, power[2]); + rtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg); + reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); + reg = RW(reg, R92C_TXAGC_B_CCK11, power[3]); + rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); + } + /* Write per-OFDM rate Tx power. */ + rtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain), + SM(R92C_TXAGC_RATE06, power[ 4]) | + SM(R92C_TXAGC_RATE09, power[ 5]) | + SM(R92C_TXAGC_RATE12, power[ 6]) | + SM(R92C_TXAGC_RATE18, power[ 7])); + rtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain), + SM(R92C_TXAGC_RATE24, power[ 8]) | + SM(R92C_TXAGC_RATE36, power[ 9]) | + SM(R92C_TXAGC_RATE48, power[10]) | + SM(R92C_TXAGC_RATE54, power[11])); + /* Write per-MCS Tx power. */ + rtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain), + SM(R92C_TXAGC_MCS00, power[12]) | + SM(R92C_TXAGC_MCS01, power[13]) | + SM(R92C_TXAGC_MCS02, power[14]) | + SM(R92C_TXAGC_MCS03, power[15])); + rtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain), + SM(R92C_TXAGC_MCS04, power[16]) | + SM(R92C_TXAGC_MCS05, power[17]) | + SM(R92C_TXAGC_MCS06, power[18]) | + SM(R92C_TXAGC_MCS07, power[19])); + rtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain), + SM(R92C_TXAGC_MCS08, power[20]) | + SM(R92C_TXAGC_MCS09, power[21]) | + SM(R92C_TXAGC_MCS10, power[22]) | + SM(R92C_TXAGC_MCS11, power[23])); + rtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain), + SM(R92C_TXAGC_MCS12, power[24]) | + SM(R92C_TXAGC_MCS13, power[25]) | + SM(R92C_TXAGC_MCS14, power[26]) | + SM(R92C_TXAGC_MCS15, power[27])); +} + +static void +rtwn_get_txpower(struct rtwn_softc *sc, int chain, + struct ieee80211_channel *c, struct ieee80211_channel *extc, + uint16_t power[RTWN_RIDX_COUNT]) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct r92c_rom *rom = &sc->rom; + uint16_t cckpow, ofdmpow, htpow, diff, max; + const struct rtwn_txpwr *base; + int ridx, chan, group; + + /* Determine channel group. */ + chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ + if (chan <= 3) + group = 0; + else if (chan <= 9) + group = 1; + else + group = 2; + + /* Get original Tx power based on board type and RF chain. */ + if (!(sc->chip & RTWN_CHIP_92C)) { + if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) + base = &rtl8188ru_txagc[chain]; + else + base = &rtl8192cu_txagc[chain]; + } else + base = &rtl8192cu_txagc[chain]; + + memset(power, 0, RTWN_RIDX_COUNT * sizeof(power[0])); + if (sc->regulatory == 0) { + for (ridx = 0; ridx <= 3; ridx++) + power[ridx] = base->pwr[0][ridx]; + } + for (ridx = 4; ridx < RTWN_RIDX_COUNT; ridx++) { + if (sc->regulatory == 3) { + power[ridx] = base->pwr[0][ridx]; + /* Apply vendor limits. */ + if (extc != NULL) + max = rom->ht40_max_pwr[group]; + else + max = rom->ht20_max_pwr[group]; + max = (max >> (chain * 4)) & 0xf; + if (power[ridx] > max) + power[ridx] = max; + } else if (sc->regulatory == 1) { + if (extc == NULL) + power[ridx] = base->pwr[group][ridx]; + } else if (sc->regulatory != 2) + power[ridx] = base->pwr[0][ridx]; + } + + /* Compute per-CCK rate Tx power. */ + cckpow = rom->cck_tx_pwr[chain][group]; + for (ridx = 0; ridx <= 3; ridx++) { + power[ridx] += cckpow; + if (power[ridx] > R92C_MAX_TX_PWR) + power[ridx] = R92C_MAX_TX_PWR; + } + + htpow = rom->ht40_1s_tx_pwr[chain][group]; + if (sc->ntxchains > 1) { + /* Apply reduction for 2 spatial streams. */ + diff = rom->ht40_2s_tx_pwr_diff[group]; + diff = (diff >> (chain * 4)) & 0xf; + htpow = (htpow > diff) ? htpow - diff : 0; + } + + /* Compute per-OFDM rate Tx power. */ + diff = rom->ofdm_tx_pwr_diff[group]; + diff = (diff >> (chain * 4)) & 0xf; + ofdmpow = htpow + diff; /* HT->OFDM correction. */ + for (ridx = 4; ridx <= 11; ridx++) { + power[ridx] += ofdmpow; + if (power[ridx] > R92C_MAX_TX_PWR) + power[ridx] = R92C_MAX_TX_PWR; + } + + /* Compute per-MCS Tx power. */ + if (extc == NULL) { + diff = rom->ht20_tx_pwr_diff[group]; + diff = (diff >> (chain * 4)) & 0xf; + htpow += diff; /* HT40->HT20 correction. */ + } + for (ridx = 12; ridx <= 27; ridx++) { + power[ridx] += htpow; + if (power[ridx] > R92C_MAX_TX_PWR) + power[ridx] = R92C_MAX_TX_PWR; + } +#ifdef RTWN_DEBUG + if (sc->sc_debug >= 4) { + /* Dump per-rate Tx power values. */ + printf("Tx power for chain %d:\n", chain); + for (ridx = 0; ridx < RTWN_RIDX_COUNT; ridx++) + printf("Rate %d = %u\n", ridx, power[ridx]); + } +#endif +} + +static void +rtwn_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c, + struct ieee80211_channel *extc) +{ + uint16_t power[RTWN_RIDX_COUNT]; + int i; + + for (i = 0; i < sc->ntxchains; i++) { + /* Compute per-rate Tx power values. */ + rtwn_get_txpower(sc, i, c, extc, power); + /* Write per-rate Tx power values to hardware. */ + rtwn_write_txpower(sc, i, power); + } +} + +static void +rtwn_scan_start(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +rtwn_scan_end(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +rtwn_set_channel(struct ieee80211com *ic) +{ + struct rtwn_softc *sc = ic->ic_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + RTWN_LOCK(sc); + if (vap->iv_state == IEEE80211_S_SCAN) { + /* Make link LED blink during scan. */ + rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); + } + rtwn_set_chan(sc, ic->ic_curchan, NULL); + RTWN_UNLOCK(sc); +} + +static void +rtwn_update_mcast(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +rtwn_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c, + struct ieee80211_channel *extc) +{ + struct ieee80211com *ic = &sc->sc_ic; + u_int chan; + int i; + + chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ + if (chan == 0 || chan == IEEE80211_CHAN_ANY) { + device_printf(sc->sc_dev, + "%s: invalid channel %x\n", __func__, chan); + return; + } + + /* Set Tx power for this new channel. */ + rtwn_set_txpower(sc, c, extc); + + for (i = 0; i < sc->nrxchains; i++) { + rtwn_rf_write(sc, i, R92C_RF_CHNLBW, + RW(sc->rf_chnlbw[i], R92C_RF_CHNLBW_CHNL, chan)); + } +#ifndef IEEE80211_NO_HT + if (extc != NULL) { + uint32_t reg; + + /* Is secondary channel below or above primary? */ + int prichlo = c->ic_freq < extc->ic_freq; + + rtwn_write_1(sc, R92C_BWOPMODE, + rtwn_read_1(sc, R92C_BWOPMODE) & ~R92C_BWOPMODE_20MHZ); + + reg = rtwn_read_1(sc, R92C_RRSR + 2); + reg = (reg & ~0x6f) | (prichlo ? 1 : 2) << 5; + rtwn_write_1(sc, R92C_RRSR + 2, reg); + + rtwn_bb_write(sc, R92C_FPGA0_RFMOD, + rtwn_bb_read(sc, R92C_FPGA0_RFMOD) | R92C_RFMOD_40MHZ); + rtwn_bb_write(sc, R92C_FPGA1_RFMOD, + rtwn_bb_read(sc, R92C_FPGA1_RFMOD) | R92C_RFMOD_40MHZ); + + /* Set CCK side band. */ + reg = rtwn_bb_read(sc, R92C_CCK0_SYSTEM); + reg = (reg & ~0x00000010) | (prichlo ? 0 : 1) << 4; + rtwn_bb_write(sc, R92C_CCK0_SYSTEM, reg); + + reg = rtwn_bb_read(sc, R92C_OFDM1_LSTF); + reg = (reg & ~0x00000c00) | (prichlo ? 1 : 2) << 10; + rtwn_bb_write(sc, R92C_OFDM1_LSTF, reg); + + rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, + rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) & + ~R92C_FPGA0_ANAPARAM2_CBW20); + + reg = rtwn_bb_read(sc, 0x818); + reg = (reg & ~0x0c000000) | (prichlo ? 2 : 1) << 26; + rtwn_bb_write(sc, 0x818, reg); + + /* Select 40MHz bandwidth. */ + rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, + (sc->rf_chnlbw[0] & ~0xfff) | chan); + } else +#endif + { + rtwn_write_1(sc, R92C_BWOPMODE, + rtwn_read_1(sc, R92C_BWOPMODE) | R92C_BWOPMODE_20MHZ); + + rtwn_bb_write(sc, R92C_FPGA0_RFMOD, + rtwn_bb_read(sc, R92C_FPGA0_RFMOD) & ~R92C_RFMOD_40MHZ); + rtwn_bb_write(sc, R92C_FPGA1_RFMOD, + rtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ); + + rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, + rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) | + R92C_FPGA0_ANAPARAM2_CBW20); + + /* Select 20MHz bandwidth. */ + rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, + (sc->rf_chnlbw[0] & ~0xfff) | R92C_RF_CHNLBW_BW20 | chan); + } +} + +static int +rtwn_iq_calib_chain(struct rtwn_softc *sc, int chain, uint16_t tx[2], + uint16_t rx[2]) +{ + uint32_t status; + int offset = chain * 0x20; + + if (chain == 0) { /* IQ calibration for chain 0. */ + /* IQ calibration settings for chain 0. */ + rtwn_bb_write(sc, 0xe30, 0x10008c1f); + rtwn_bb_write(sc, 0xe34, 0x10008c1f); + rtwn_bb_write(sc, 0xe38, 0x82140102); + + if (sc->ntxchains > 1) { + rtwn_bb_write(sc, 0xe3c, 0x28160202); /* 2T */ + /* IQ calibration settings for chain 1. */ + rtwn_bb_write(sc, 0xe50, 0x10008c22); + rtwn_bb_write(sc, 0xe54, 0x10008c22); + rtwn_bb_write(sc, 0xe58, 0x82140102); + rtwn_bb_write(sc, 0xe5c, 0x28160202); + } else + rtwn_bb_write(sc, 0xe3c, 0x28160502); /* 1T */ + + /* LO calibration settings. */ + rtwn_bb_write(sc, 0xe4c, 0x001028d1); + /* We're doing LO and IQ calibration in one shot. */ + rtwn_bb_write(sc, 0xe48, 0xf9000000); + rtwn_bb_write(sc, 0xe48, 0xf8000000); + + } else { /* IQ calibration for chain 1. */ + /* We're doing LO and IQ calibration in one shot. */ + rtwn_bb_write(sc, 0xe60, 0x00000002); + rtwn_bb_write(sc, 0xe60, 0x00000000); + } + + /* Give LO and IQ calibrations the time to complete. */ + DELAY(1000); + + /* Read IQ calibration status. */ + status = rtwn_bb_read(sc, 0xeac); + + if (status & (1 << (28 + chain * 3))) + return (0); /* Tx failed. */ + /* Read Tx IQ calibration results. */ + tx[0] = (rtwn_bb_read(sc, 0xe94 + offset) >> 16) & 0x3ff; + tx[1] = (rtwn_bb_read(sc, 0xe9c + offset) >> 16) & 0x3ff; + if (tx[0] == 0x142 || tx[1] == 0x042) + return (0); /* Tx failed. */ + + if (status & (1 << (27 + chain * 3))) + return (1); /* Rx failed. */ + /* Read Rx IQ calibration results. */ + rx[0] = (rtwn_bb_read(sc, 0xea4 + offset) >> 16) & 0x3ff; + rx[1] = (rtwn_bb_read(sc, 0xeac + offset) >> 16) & 0x3ff; + if (rx[0] == 0x132 || rx[1] == 0x036) + return (1); /* Rx failed. */ + + return (3); /* Both Tx and Rx succeeded. */ +} + +static void +rtwn_iq_calib_run(struct rtwn_softc *sc, int n, uint16_t tx[2][2], + uint16_t rx[2][2]) +{ + /* Registers to save and restore during IQ calibration. */ + struct iq_cal_regs { + uint32_t adda[16]; + uint8_t txpause; + uint8_t bcn_ctrl; + uint8_t ustime_tsf; + uint32_t gpio_muxcfg; + uint32_t ofdm0_trxpathena; + uint32_t ofdm0_trmuxpar; + uint32_t fpga0_rfifacesw1; + } iq_cal_regs; + static const uint16_t reg_adda[16] = { + 0x85c, 0xe6c, 0xe70, 0xe74, + 0xe78, 0xe7c, 0xe80, 0xe84, + 0xe88, 0xe8c, 0xed0, 0xed4, + 0xed8, 0xedc, 0xee0, 0xeec + }; + int i, chain; + uint32_t hssi_param1; + + if (n == 0) { + for (i = 0; i < nitems(reg_adda); i++) + iq_cal_regs.adda[i] = rtwn_bb_read(sc, reg_adda[i]); + + iq_cal_regs.txpause = rtwn_read_1(sc, R92C_TXPAUSE); + iq_cal_regs.bcn_ctrl = rtwn_read_1(sc, R92C_BCN_CTRL); + iq_cal_regs.ustime_tsf = rtwn_read_1(sc, R92C_USTIME_TSF); + iq_cal_regs.gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG); + } + + if (sc->ntxchains == 1) { + rtwn_bb_write(sc, reg_adda[0], 0x0b1b25a0); + for (i = 1; i < nitems(reg_adda); i++) + rtwn_bb_write(sc, reg_adda[i], 0x0bdb25a0); + } else { + for (i = 0; i < nitems(reg_adda); i++) + rtwn_bb_write(sc, reg_adda[i], 0x04db25a4); + } + + hssi_param1 = rtwn_bb_read(sc, R92C_HSSI_PARAM1(0)); + if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { + rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), + hssi_param1 | R92C_HSSI_PARAM1_PI); + rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), + hssi_param1 | R92C_HSSI_PARAM1_PI); + } + + if (n == 0) { + iq_cal_regs.ofdm0_trxpathena = + rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); + iq_cal_regs.ofdm0_trmuxpar = + rtwn_bb_read(sc, R92C_OFDM0_TRMUXPAR); + iq_cal_regs.fpga0_rfifacesw1 = + rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(1)); + } + + rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600); + rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4); + rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000); + if (sc->ntxchains > 1) { + rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); + rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00010000); + } + + rtwn_write_1(sc, R92C_TXPAUSE, 0x3f); + rtwn_write_1(sc, R92C_BCN_CTRL, iq_cal_regs.bcn_ctrl & ~(0x08)); + rtwn_write_1(sc, R92C_USTIME_TSF, iq_cal_regs.ustime_tsf & ~(0x08)); + rtwn_write_1(sc, R92C_GPIO_MUXCFG, + iq_cal_regs.gpio_muxcfg & ~(0x20)); + + rtwn_bb_write(sc, 0x0b68, 0x00080000); + if (sc->ntxchains > 1) + rtwn_bb_write(sc, 0x0b6c, 0x00080000); + + rtwn_bb_write(sc, 0x0e28, 0x80800000); + rtwn_bb_write(sc, 0x0e40, 0x01007c00); + rtwn_bb_write(sc, 0x0e44, 0x01004800); + + rtwn_bb_write(sc, 0x0b68, 0x00080000); + + for (chain = 0; chain < sc->ntxchains; chain++) { + if (chain > 0) { + /* Put chain 0 on standby. */ + rtwn_bb_write(sc, 0x0e28, 0x00); + rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); + rtwn_bb_write(sc, 0x0e28, 0x80800000); + + /* Enable chain 1. */ + for (i = 0; i < nitems(reg_adda); i++) + rtwn_bb_write(sc, reg_adda[i], 0x0b1b25a4); + } + + /* Run IQ calibration twice. */ + for (i = 0; i < 2; i++) { + int ret; + + ret = rtwn_iq_calib_chain(sc, chain, + tx[chain], rx[chain]); + if (ret == 0) { + DPRINTF(("%s: chain %d: Tx failed.\n", + __func__, chain)); + tx[chain][0] = 0xff; + tx[chain][1] = 0xff; + rx[chain][0] = 0xff; + rx[chain][1] = 0xff; + } else if (ret == 1) { + DPRINTF(("%s: chain %d: Rx failed.\n", + __func__, chain)); + rx[chain][0] = 0xff; + rx[chain][1] = 0xff; + } else if (ret == 3) { + DPRINTF(("%s: chain %d: Both Tx and Rx " + "succeeded.\n", __func__, chain)); + } + } + + DPRINTF(("%s: results for run %d chain %d: tx[0]=0x%x, " + "tx[1]=0x%x rx[0]=0x%x rx[1]=0x%x\n", __func__, n, chain, + tx[chain][0], tx[chain][1], rx[chain][0], rx[chain][1])); + } + + rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, + iq_cal_regs.ofdm0_trxpathena); + rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), + iq_cal_regs.fpga0_rfifacesw1); + rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, iq_cal_regs.ofdm0_trmuxpar); + + rtwn_bb_write(sc, 0x0e28, 0x00); + rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00032ed3); + if (sc->ntxchains > 1) + rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00032ed3); + + if (n != 0) { + if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { + rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), hssi_param1); + rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), hssi_param1); + } + + for (i = 0; i < nitems(reg_adda); i++) + rtwn_bb_write(sc, reg_adda[i], iq_cal_regs.adda[i]); + + rtwn_write_1(sc, R92C_TXPAUSE, iq_cal_regs.txpause); + rtwn_write_1(sc, R92C_BCN_CTRL, iq_cal_regs.bcn_ctrl); + rtwn_write_1(sc, R92C_USTIME_TSF, iq_cal_regs.ustime_tsf); + rtwn_write_4(sc, R92C_GPIO_MUXCFG, iq_cal_regs.gpio_muxcfg); + } +} + +#define RTWN_IQ_CAL_MAX_TOLERANCE 5 +static int +rtwn_iq_calib_compare_results(uint16_t tx1[2][2], uint16_t rx1[2][2], + uint16_t tx2[2][2], uint16_t rx2[2][2], int ntxchains) +{ + int chain, i, tx_ok[2], rx_ok[2]; + + tx_ok[0] = tx_ok[1] = rx_ok[0] = rx_ok[1] = 0; + for (chain = 0; chain < ntxchains; chain++) { + for (i = 0; i < 2; i++) { + if (tx1[chain][i] == 0xff || tx2[chain][i] == 0xff || + rx1[chain][i] == 0xff || rx2[chain][i] == 0xff) + continue; + + tx_ok[chain] = (abs(tx1[chain][i] - tx2[chain][i]) <= + RTWN_IQ_CAL_MAX_TOLERANCE); + + rx_ok[chain] = (abs(rx1[chain][i] - rx2[chain][i]) <= + RTWN_IQ_CAL_MAX_TOLERANCE); + } + } + + if (ntxchains > 1) + return (tx_ok[0] && tx_ok[1] && rx_ok[0] && rx_ok[1]); + else + return (tx_ok[0] && rx_ok[0]); +} +#undef RTWN_IQ_CAL_MAX_TOLERANCE + +static void +rtwn_iq_calib_write_results(struct rtwn_softc *sc, uint16_t tx[2], + uint16_t rx[2], int chain) +{ + uint32_t reg, val, x; + long y, tx_c; + + if (tx[0] == 0xff || tx[1] == 0xff) + return; + + reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); + val = ((reg >> 22) & 0x3ff); + x = tx[0]; + if (x & 0x0200) + x |= 0xfc00; + reg = (((x * val) >> 8) & 0x3ff); + rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); + if (((x * val) >> 7) & 0x01) + reg |= 0x80000000; + else + reg &= ~0x80000000; + rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); + + y = tx[1]; + if (y & 0x00000200) + y |= 0xfffffc00; + tx_c = (y * val) >> 8; + reg = rtwn_bb_read(sc, R92C_OFDM0_TXAFE(chain)); + reg |= ((((tx_c & 0x3c0) >> 6) << 24) & 0xf0000000); + rtwn_bb_write(sc, R92C_OFDM0_TXAFE(chain), reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); + reg |= (((tx_c & 0x3f) << 16) & 0x003F0000); + rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); + + reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); + if (((y * val) >> 7) & 0x01) + reg |= 0x20000000; + else + reg &= ~0x20000000; + rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); + + if (rx[0] == 0xff || rx[1] == 0xff) + return; + + reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQIMBALANCE(chain)); + reg |= (rx[0] & 0x3ff); + rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); + reg |= (((rx[1] & 0x03f) << 8) & 0xFC00); + rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); + + if (chain == 0) { + reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQEXTANTA); + reg |= (((rx[1] & 0xf) >> 6) & 0x000f); + rtwn_bb_write(sc, R92C_OFDM0_RXIQEXTANTA, reg); + } else { + reg = rtwn_bb_read(sc, R92C_OFDM0_AGCRSSITABLE); + reg |= ((((rx[1] & 0xf) >> 6) << 12) & 0xf000); + rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, reg); + } +} + +#define RTWN_IQ_CAL_NRUN 3 +static void +rtwn_iq_calib(struct rtwn_softc *sc) +{ + uint16_t tx[RTWN_IQ_CAL_NRUN][2][2], rx[RTWN_IQ_CAL_NRUN][2][2]; + int n, valid; + + valid = 0; + for (n = 0; n < RTWN_IQ_CAL_NRUN; n++) { + rtwn_iq_calib_run(sc, n, tx[n], rx[n]); + + if (n == 0) + continue; + + /* Valid results remain stable after consecutive runs. */ + valid = rtwn_iq_calib_compare_results(tx[n - 1], rx[n - 1], + tx[n], rx[n], sc->ntxchains); + if (valid) + break; + } + + if (valid) { + rtwn_iq_calib_write_results(sc, tx[n][0], rx[n][0], 0); + if (sc->ntxchains > 1) + rtwn_iq_calib_write_results(sc, tx[n][1], rx[n][1], 1); + } +} +#undef RTWN_IQ_CAL_NRUN + +static void +rtwn_lc_calib(struct rtwn_softc *sc) +{ + uint32_t rf_ac[2]; + uint8_t txmode; + int i; + + txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3); + if ((txmode & 0x70) != 0) { + /* Disable all continuous Tx. */ + rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70); + + /* Set RF mode to standby mode. */ + for (i = 0; i < sc->nrxchains; i++) { + rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC); + rtwn_rf_write(sc, i, R92C_RF_AC, + RW(rf_ac[i], R92C_RF_AC_MODE, + R92C_RF_AC_MODE_STANDBY)); + } + } else { + /* Block all Tx queues. */ + rtwn_write_1(sc, R92C_TXPAUSE, 0xff); + } + /* Start calibration. */ + rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, + rtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART); + + /* Give calibration the time to complete. */ + DELAY(100); + + /* Restore configuration. */ + if ((txmode & 0x70) != 0) { + /* Restore Tx mode. */ + rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode); + /* Restore RF mode. */ + for (i = 0; i < sc->nrxchains; i++) + rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]); + } else { + /* Unblock all Tx queues. */ + rtwn_write_1(sc, R92C_TXPAUSE, 0x00); + } +} + +static void +rtwn_temp_calib(struct rtwn_softc *sc) +{ + int temp; + + if (sc->thcal_state == 0) { + /* Start measuring temperature. */ + rtwn_rf_write(sc, 0, R92C_RF_T_METER, 0x60); + sc->thcal_state = 1; + return; + } + sc->thcal_state = 0; + + /* Read measured temperature. */ + temp = rtwn_rf_read(sc, 0, R92C_RF_T_METER) & 0x1f; + if (temp == 0) /* Read failed, skip. */ + return; + DPRINTFN(2, ("temperature=%d\n", temp)); + + /* + * Redo IQ and LC calibration if temperature changed significantly + * since last calibration. + */ + if (sc->thcal_lctemp == 0) { + /* First calibration is performed in rtwn_init(). */ + sc->thcal_lctemp = temp; + } else if (abs(temp - sc->thcal_lctemp) > 1) { + DPRINTF(("IQ/LC calib triggered by temp: %d -> %d\n", + sc->thcal_lctemp, temp)); + rtwn_iq_calib(sc); + rtwn_lc_calib(sc); + /* Record temperature of last calibration. */ + sc->thcal_lctemp = temp; + } +} + +static void +rtwn_init_locked(struct rtwn_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t reg; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + int i, error; + + RTWN_LOCK_ASSERT(sc); + + /* Init firmware commands ring. */ + sc->fwcur = 0; + + /* Power on adapter. */ + error = rtwn_power_on(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not power on adapter\n"); + goto fail; + } + + /* Initialize DMA. */ + error = rtwn_dma_init(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not initialize DMA\n"); + goto fail; + } + + /* Set info size in Rx descriptors (in 64-bit words). */ + rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4); + + /* Disable interrupts. */ + rtwn_write_4(sc, R92C_HISR, 0x00000000); + rtwn_write_4(sc, R92C_HIMR, 0x00000000); + + /* Set MAC address. */ + IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + rtwn_write_1(sc, R92C_MACID + i, macaddr[i]); + + /* Set initial network type. */ + reg = rtwn_read_4(sc, R92C_CR); + reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA); + rtwn_write_4(sc, R92C_CR, reg); + + rtwn_rxfilter_init(sc); + + reg = rtwn_read_4(sc, R92C_RRSR); + reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_ALL); + rtwn_write_4(sc, R92C_RRSR, reg); + + /* Set short/long retry limits. */ + rtwn_write_2(sc, R92C_RL, + SM(R92C_RL_SRL, 0x07) | SM(R92C_RL_LRL, 0x07)); + + /* Initialize EDCA parameters. */ + rtwn_edca_init(sc); + + /* Set data and response automatic rate fallback retry counts. */ + rtwn_write_4(sc, R92C_DARFRC + 0, 0x01000000); + rtwn_write_4(sc, R92C_DARFRC + 4, 0x07060504); + rtwn_write_4(sc, R92C_RARFRC + 0, 0x01000000); + rtwn_write_4(sc, R92C_RARFRC + 4, 0x07060504); + + rtwn_write_2(sc, R92C_FWHW_TXQ_CTRL, 0x1f80); + + /* Set ACK timeout. */ + rtwn_write_1(sc, R92C_ACKTO, 0x40); + + /* Initialize beacon parameters. */ + rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404); + rtwn_write_1(sc, R92C_DRVERLYINT, 0x05); + rtwn_write_1(sc, R92C_BCNDMATIM, 0x02); + rtwn_write_2(sc, R92C_BCNTCFG, 0x660f); + + /* Setup AMPDU aggregation. */ + rtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */ + rtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16); + + rtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff); + rtwn_write_1(sc, R92C_BCN_CTRL, R92C_BCN_CTRL_DIS_TSF_UDT0); + + rtwn_write_4(sc, R92C_PIFS, 0x1c); + rtwn_write_4(sc, R92C_MCUTST_1, 0x0); + + /* Load 8051 microcode. */ + error = rtwn_load_firmware(sc); + if (error != 0) + goto fail; + + /* Initialize MAC/BB/RF blocks. */ + rtwn_mac_init(sc); + rtwn_bb_init(sc); + rtwn_rf_init(sc); + + /* Turn CCK and OFDM blocks on. */ + reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); + reg |= R92C_RFMOD_CCK_EN; + rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); + reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); + reg |= R92C_RFMOD_OFDM_EN; + rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); + + /* Clear per-station keys table. */ + rtwn_cam_init(sc); + + /* Enable hardware sequence numbering. */ + rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff); + + /* Perform LO and IQ calibrations. */ + rtwn_iq_calib(sc); + /* Perform LC calibration. */ + rtwn_lc_calib(sc); + + rtwn_pa_bias_init(sc); + + /* Initialize GPIO setting. */ + rtwn_write_1(sc, R92C_GPIO_MUXCFG, + rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT); + + /* Fix for lower temperature. */ + rtwn_write_1(sc, 0x15, 0xe9); + + /* CLear pending interrupts. */ + rtwn_write_4(sc, R92C_HISR, 0xffffffff); + + /* Enable interrupts. */ + rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE); + + sc->sc_flags |= RTWN_RUNNING; + + callout_reset(&sc->watchdog_to, hz, rtwn_watchdog, sc); + return; + +fail: + rtwn_stop_locked(sc); +} + +static void +rtwn_init(struct rtwn_softc *sc) +{ + + RTWN_LOCK(sc); + rtwn_init_locked(sc); + RTWN_UNLOCK(sc); + + if (sc->sc_flags & RTWN_RUNNING) + ieee80211_start_all(&sc->sc_ic); +} + +static void +rtwn_stop_locked(struct rtwn_softc *sc) +{ + uint16_t reg; + int i; + + RTWN_LOCK_ASSERT(sc); + + sc->sc_tx_timer = 0; + callout_stop(&sc->watchdog_to); + callout_stop(&sc->calib_to); + sc->sc_flags &= ~RTWN_RUNNING; + + /* Disable interrupts. */ + rtwn_write_4(sc, R92C_HISR, 0x00000000); + rtwn_write_4(sc, R92C_HIMR, 0x00000000); + + /* Stop hardware. */ + rtwn_write_1(sc, R92C_TXPAUSE, 0xff); + rtwn_write_1(sc, R92C_RF_CTRL, 0x00); + reg = rtwn_read_1(sc, R92C_SYS_FUNC_EN); + reg |= R92C_SYS_FUNC_EN_BB_GLB_RST; + rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg); + reg &= ~R92C_SYS_FUNC_EN_BB_GLB_RST; + rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg); + reg = rtwn_read_2(sc, R92C_CR); + reg &= ~(R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | + R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | + R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | + R92C_CR_ENSEC); + rtwn_write_2(sc, R92C_CR, reg); + if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) + rtwn_fw_reset(sc); + /* TODO: linux does additional btcoex stuff here */ + rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0x80); /* linux magic number */ + rtwn_write_1(sc, R92C_SPS0_CTRL, 0x23); /* ditto */ + rtwn_write_1(sc, R92C_AFE_XTAL_CTRL, 0x0e); /* different with btcoex */ + rtwn_write_1(sc, R92C_RSV_CTRL, 0x0e); + rtwn_write_1(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_PDN_EN); + + for (i = 0; i < RTWN_NTXQUEUES; i++) + rtwn_reset_tx_list(sc, i); + rtwn_reset_rx_list(sc); +} + +static void +rtwn_stop(struct rtwn_softc *sc) +{ + RTWN_LOCK(sc); + rtwn_stop_locked(sc); + RTWN_UNLOCK(sc); +} + +static void +rtwn_intr(void *arg) +{ + struct rtwn_softc *sc = arg; + uint32_t status; + int i; + + RTWN_LOCK(sc); + status = rtwn_read_4(sc, R92C_HISR); + if (status == 0 || status == 0xffffffff) { + RTWN_UNLOCK(sc); + return; + } + + /* Disable interrupts. */ + rtwn_write_4(sc, R92C_HIMR, 0x00000000); + + /* Ack interrupts. */ + rtwn_write_4(sc, R92C_HISR, status); + + /* Vendor driver treats RX errors like ROK... */ + if (status & (R92C_IMR_ROK | R92C_IMR_RXFOVW | R92C_IMR_RDU)) { + bus_dmamap_sync(sc->rx_ring.desc_dmat, sc->rx_ring.desc_map, + BUS_DMASYNC_POSTREAD); + + for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { + struct r92c_rx_desc *rx_desc = &sc->rx_ring.desc[i]; + struct rtwn_rx_data *rx_data = &sc->rx_ring.rx_data[i]; + + if (le32toh(rx_desc->rxdw0) & R92C_RXDW0_OWN) + continue; + + rtwn_rx_frame(sc, rx_desc, rx_data, i); + } + } + + if (status & R92C_IMR_BDOK) + rtwn_tx_done(sc, RTWN_BEACON_QUEUE); + if (status & R92C_IMR_HIGHDOK) + rtwn_tx_done(sc, RTWN_HIGH_QUEUE); + if (status & R92C_IMR_MGNTDOK) + rtwn_tx_done(sc, RTWN_MGNT_QUEUE); + if (status & R92C_IMR_BKDOK) + rtwn_tx_done(sc, RTWN_BK_QUEUE); + if (status & R92C_IMR_BEDOK) + rtwn_tx_done(sc, RTWN_BE_QUEUE); + if (status & R92C_IMR_VIDOK) + rtwn_tx_done(sc, RTWN_VI_QUEUE); + if (status & R92C_IMR_VODOK) + rtwn_tx_done(sc, RTWN_VO_QUEUE); + + /* Enable interrupts. */ + rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE); + + RTWN_UNLOCK(sc); +} + +static void +rtwn_hw_reset(void *arg0, int pending) +{ + struct rtwn_softc *sc = arg0; + struct ieee80211com *ic = &sc->sc_ic; + + rtwn_stop(sc); + rtwn_init(sc); + ieee80211_notify_radio(ic, 1); +} Property changes on: projects/clang380-import/sys/dev/rtwn/if_rtwn.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/dev/rtwn/if_rtwnreg.h =================================================================== --- projects/clang380-import/sys/dev/rtwn/if_rtwnreg.h (nonexistent) +++ projects/clang380-import/sys/dev/rtwn/if_rtwnreg.h (revision 293016) @@ -0,0 +1,2106 @@ +/* $OpenBSD: if_rtwnreg.h,v 1.3 2015/06/14 08:02:47 stsp Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini + * Copyright (c) 2015 Stefan Sperling + * + * 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. + * + * $FreeBSD$ + */ + +#define R92C_MAX_CHAINS 2 + +/* Maximum number of output pipes is 3. */ +#define R92C_MAX_EPOUT 3 + +#define R92C_MAX_TX_PWR 0x3f + +#define R92C_PUBQ_NPAGES 176 +#define R92C_HPQ_NPAGES 41 +#define R92C_LPQ_NPAGES 28 +#define R92C_TXPKTBUF_COUNT 256 +#define R92C_TX_PAGE_COUNT \ + (R92C_PUBQ_NPAGES + R92C_HPQ_NPAGES + R92C_LPQ_NPAGES) +#define R92C_TX_PAGE_BOUNDARY (R92C_TX_PAGE_COUNT + 1) + +#define R92C_H2C_NBOX 4 + +/* USB Requests. */ +#define R92C_REQ_REGS 0x05 + +/* + * MAC registers. + */ +/* System Configuration. */ +#define R92C_SYS_ISO_CTRL 0x000 +#define R92C_SYS_FUNC_EN 0x002 +#define R92C_APS_FSMCO 0x004 +#define R92C_SYS_CLKR 0x008 +#define R92C_AFE_MISC 0x010 +#define R92C_SPS0_CTRL 0x011 +#define R92C_SPS_OCP_CFG 0x018 +#define R92C_RSV_CTRL 0x01c +#define R92C_RF_CTRL 0x01f +#define R92C_LDOA15_CTRL 0x020 +#define R92C_LDOV12D_CTRL 0x021 +#define R92C_LDOHCI12_CTRL 0x022 +#define R92C_LPLDO_CTRL 0x023 +#define R92C_AFE_XTAL_CTRL 0x024 +#define R92C_AFE_PLL_CTRL 0x028 +#define R92C_EFUSE_CTRL 0x030 +#define R92C_EFUSE_TEST 0x034 +#define R92C_PWR_DATA 0x038 +#define R92C_CAL_TIMER 0x03c +#define R92C_ACLK_MON 0x03e +#define R92C_GPIO_MUXCFG 0x040 +#define R92C_GPIO_IO_SEL 0x042 +#define R92C_MAC_PINMUX_CFG 0x043 +#define R92C_GPIO_PIN_CTRL 0x044 +#define R92C_GPIO_INTM 0x048 +#define R92C_LEDCFG0 0x04c +#define R92C_LEDCFG1 0x04d +#define R92C_LEDCFG2 0x04e +#define R92C_LEDCFG3 0x04f +#define R92C_FSIMR 0x050 +#define R92C_FSISR 0x054 +#define R92C_HSIMR 0x058 +#define R92C_HSISR 0x05c +#define R92C_MCUFWDL 0x080 +#define R92C_HMEBOX_EXT(idx) (0x088 + (idx) * 2) +#define R92C_BIST_SCAN 0x0d0 +#define R92C_BIST_RPT 0x0d4 +#define R92C_BIST_ROM_RPT 0x0d8 +#define R92C_USB_SIE_INTF 0x0e0 +#define R92C_PCIE_MIO_INTF 0x0e4 +#define R92C_PCIE_MIO_INTD 0x0e8 +#define R92C_HPON_FSM 0x0ec +#define R92C_SYS_CFG 0x0f0 +/* MAC General Configuration. */ +#define R92C_CR 0x100 +#define R92C_PBP 0x104 +#define R92C_TRXDMA_CTRL 0x10c +#define R92C_TRXFF_BNDY 0x114 +#define R92C_TRXFF_STATUS 0x118 +#define R92C_RXFF_PTR 0x11c +#define R92C_HIMR 0x120 +#define R92C_HISR 0x124 +#define R92C_HIMRE 0x128 +#define R92C_HISRE 0x12c +#define R92C_CPWM 0x12f +#define R92C_FWIMR 0x130 +#define R92C_FWISR 0x134 +#define R92C_PKTBUF_DBG_CTRL 0x140 +#define R92C_PKTBUF_DBG_DATA_L 0x144 +#define R92C_PKTBUF_DBG_DATA_H 0x148 +#define R92C_TC0_CTRL(i) (0x150 + (i) * 4) +#define R92C_TCUNIT_BASE 0x164 +#define R92C_MBIST_START 0x174 +#define R92C_MBIST_DONE 0x178 +#define R92C_MBIST_FAIL 0x17c +#define R92C_C2HEVT_MSG_NORMAL 0x1a0 +#define R92C_C2HEVT_MSG_TEST 0x1b8 +#define R92C_C2HEVT_CLEAR 0x1bf +#define R92C_MCUTST_1 0x1c0 +#define R92C_FMETHR 0x1c8 +#define R92C_HMETFR 0x1cc +#define R92C_HMEBOX(idx) (0x1d0 + (idx) * 4) +#define R92C_LLT_INIT 0x1e0 +#define R92C_BB_ACCESS_CTRL 0x1e8 +#define R92C_BB_ACCESS_DATA 0x1ec +/* Tx DMA Configuration. */ +#define R92C_RQPN 0x200 +#define R92C_FIFOPAGE 0x204 +#define R92C_TDECTRL 0x208 +#define R92C_TXDMA_OFFSET_CHK 0x20c +#define R92C_TXDMA_STATUS 0x210 +#define R92C_RQPN_NPQ 0x214 +/* Rx DMA Configuration. */ +#define R92C_RXDMA_AGG_PG_TH 0x280 +#define R92C_RXPKT_NUM 0x284 +#define R92C_RXDMA_STATUS 0x288 + +#define R92C_PCIE_CTRL_REG 0x300 +#define R92C_INT_MIG 0x304 +#define R92C_BCNQ_DESA 0x308 +#define R92C_HQ_DESA 0x310 +#define R92C_MGQ_DESA 0x318 +#define R92C_VOQ_DESA 0x320 +#define R92C_VIQ_DESA 0x328 +#define R92C_BEQ_DESA 0x330 +#define R92C_BKQ_DESA 0x338 +#define R92C_RX_DESA 0x340 +#define R92C_DBI 0x348 +#define R92C_MDIO 0x354 +#define R92C_DBG_SEL 0x360 +#define R92C_PCIE_HRPWM 0x361 +#define R92C_PCIE_HCPWM 0x363 +#define R92C_UART_CTRL 0x364 +#define R92C_UART_TX_DES 0x370 +#define R92C_UART_RX_DES 0x378 + +#define R92C_VOQ_INFORMATION 0x0400 +#define R92C_VIQ_INFORMATION 0x0404 +#define R92C_BEQ_INFORMATION 0x0408 +#define R92C_BKQ_INFORMATION 0x040C +#define R92C_MGQ_INFORMATION 0x0410 +#define R92C_HGQ_INFORMATION 0x0414 +#define R92C_BCNQ_INFORMATION 0x0418 +#define R92C_CPU_MGQ_INFORMATION 0x041C + +/* Protocol Configuration. */ +#define R92C_FWHW_TXQ_CTRL 0x420 +#define R92C_HWSEQ_CTRL 0x423 +#define R92C_TXPKTBUF_BCNQ_BDNY 0x424 +#define R92C_TXPKTBUF_MGQ_BDNY 0x425 +#define R92C_SPEC_SIFS 0x428 +#define R92C_RL 0x42a +#define R92C_DARFRC 0x430 +#define R92C_RARFRC 0x438 +#define R92C_RRSR 0x440 +#define R92C_ARFR(i) (0x444 + (i) * 4) +#define R92C_AGGLEN_LMT 0x458 +#define R92C_AMPDU_MIN_SPACE 0x45c +#define R92C_TXPKTBUF_WMAC_LBK_BF_HD 0x45d +#define R92C_FAST_EDCA_CTRL 0x460 +#define R92C_RD_RESP_PKT_TH 0x463 +#define R92C_INIRTS_RATE_SEL 0x480 +#define R92C_INIDATA_RATE_SEL(macid) (0x484 + (macid)) +/* EDCA Configuration. */ +#define R92C_EDCA_VO_PARAM 0x500 +#define R92C_EDCA_VI_PARAM 0x504 +#define R92C_EDCA_BE_PARAM 0x508 +#define R92C_EDCA_BK_PARAM 0x50c +#define R92C_BCNTCFG 0x510 +#define R92C_PIFS 0x512 +#define R92C_RDG_PIFS 0x513 +#define R92C_SIFS_CCK 0x514 +#define R92C_SIFS_OFDM 0x516 +#define R92C_AGGR_BREAK_TIME 0x51a +#define R92C_SLOT 0x51b +#define R92C_TX_PTCL_CTRL 0x520 +#define R92C_TXPAUSE 0x522 +#define R92C_DIS_TXREQ_CLR 0x523 +#define R92C_RD_CTRL 0x524 +#define R92C_TBTT_PROHIBIT 0x540 +#define R92C_RD_NAV_NXT 0x544 +#define R92C_NAV_PROT_LEN 0x546 +#define R92C_BCN_CTRL 0x550 +#define R92C_USTIME_TSF 0x551 +#define R92C_MBID_NUM 0x552 +#define R92C_DUAL_TSF_RST 0x553 +#define R92C_BCN_INTERVAL 0x554 +#define R92C_DRVERLYINT 0x558 +#define R92C_BCNDMATIM 0x559 +#define R92C_ATIMWND 0x55a +#define R92C_BCN_MAX_ERR 0x55d +#define R92C_RXTSF_OFFSET_CCK 0x55e +#define R92C_RXTSF_OFFSET_OFDM 0x55f +#define R92C_TSFTR 0x560 +#define R92C_INIT_TSFTR 0x564 +#define R92C_PSTIMER 0x580 +#define R92C_TIMER0 0x584 +#define R92C_TIMER1 0x588 +#define R92C_ACMHWCTRL 0x5c0 +#define R92C_ACMRSTCTRL 0x5c1 +#define R92C_ACMAVG 0x5c2 +#define R92C_VO_ADMTIME 0x5c4 +#define R92C_VI_ADMTIME 0x5c6 +#define R92C_BE_ADMTIME 0x5c8 +#define R92C_EDCA_RANDOM_GEN 0x5cc +#define R92C_SCH_TXCMD 0x5d0 +/* WMAC Configuration. */ +#define R92C_APSD_CTRL 0x600 +#define R92C_BWOPMODE 0x603 +#define R92C_TCR 0x604 +#define R92C_RCR 0x608 +#define R92C_RX_DRVINFO_SZ 0x60f +#define R92C_MACID 0x610 +#define R92C_BSSID 0x618 +#define R92C_MAR 0x620 +#define R92C_MAC_SPEC_SIFS 0x63a +#define R92C_R2T_SIFS 0x63c +#define R92C_T2T_SIFS 0x63e +#define R92C_ACKTO 0x640 +#define R92C_CAMCMD 0x670 +#define R92C_CAMWRITE 0x674 +#define R92C_CAMREAD 0x678 +#define R92C_CAMDBG 0x67c +#define R92C_SECCFG 0x680 +#define R92C_RXFLTMAP0 0x6a0 +#define R92C_RXFLTMAP1 0x6a2 +#define R92C_RXFLTMAP2 0x6a4 + +/* Bits for R92C_SYS_ISO_CTRL. */ +#define R92C_SYS_ISO_CTRL_MD2PP 0x0001 +#define R92C_SYS_ISO_CTRL_UA2USB 0x0002 +#define R92C_SYS_ISO_CTRL_UD2CORE 0x0004 +#define R92C_SYS_ISO_CTRL_PA2PCIE 0x0008 +#define R92C_SYS_ISO_CTRL_PD2CORE 0x0010 +#define R92C_SYS_ISO_CTRL_IP2MAC 0x0020 +#define R92C_SYS_ISO_CTRL_DIOP 0x0040 +#define R92C_SYS_ISO_CTRL_DIOE 0x0080 +#define R92C_SYS_ISO_CTRL_EB2CORE 0x0100 +#define R92C_SYS_ISO_CTRL_DIOR 0x0200 +#define R92C_SYS_ISO_CTRL_PWC_EV25V 0x4000 +#define R92C_SYS_ISO_CTRL_PWC_EV12V 0x8000 + +/* Bits for R92C_SYS_FUNC_EN. */ +#define R92C_SYS_FUNC_EN_BBRSTB 0x0001 +#define R92C_SYS_FUNC_EN_BB_GLB_RST 0x0002 +#define R92C_SYS_FUNC_EN_USBA 0x0004 +#define R92C_SYS_FUNC_EN_UPLL 0x0008 +#define R92C_SYS_FUNC_EN_USBD 0x0010 +#define R92C_SYS_FUNC_EN_DIO_PCIE 0x0020 +#define R92C_SYS_FUNC_EN_PCIEA 0x0040 +#define R92C_SYS_FUNC_EN_PPLL 0x0080 +#define R92C_SYS_FUNC_EN_PCIED 0x0100 +#define R92C_SYS_FUNC_EN_DIOE 0x0200 +#define R92C_SYS_FUNC_EN_CPUEN 0x0400 +#define R92C_SYS_FUNC_EN_DCORE 0x0800 +#define R92C_SYS_FUNC_EN_ELDR 0x1000 +#define R92C_SYS_FUNC_EN_DIO_RF 0x2000 +#define R92C_SYS_FUNC_EN_HWPDN 0x4000 +#define R92C_SYS_FUNC_EN_MREGEN 0x8000 + +/* Bits for R92C_APS_FSMCO. */ +#define R92C_APS_FSMCO_PFM_LDALL 0x00000001 +#define R92C_APS_FSMCO_PFM_ALDN 0x00000002 +#define R92C_APS_FSMCO_PFM_LDKP 0x00000004 +#define R92C_APS_FSMCO_PFM_WOWL 0x00000008 +#define R92C_APS_FSMCO_PDN_EN 0x00000010 +#define R92C_APS_FSMCO_PDN_PL 0x00000020 +#define R92C_APS_FSMCO_APFM_ONMAC 0x00000100 +#define R92C_APS_FSMCO_APFM_OFF 0x00000200 +#define R92C_APS_FSMCO_APFM_RSM 0x00000400 +#define R92C_APS_FSMCO_AFSM_HSUS 0x00000800 +#define R92C_APS_FSMCO_AFSM_PCIE 0x00001000 +#define R92C_APS_FSMCO_APDM_MAC 0x00002000 +#define R92C_APS_FSMCO_APDM_HOST 0x00004000 +#define R92C_APS_FSMCO_APDM_HPDN 0x00008000 +#define R92C_APS_FSMCO_RDY_MACON 0x00010000 +#define R92C_APS_FSMCO_SUS_HOST 0x00020000 +#define R92C_APS_FSMCO_ROP_ALD 0x00100000 +#define R92C_APS_FSMCO_ROP_PWR 0x00200000 +#define R92C_APS_FSMCO_ROP_SPS 0x00400000 +#define R92C_APS_FSMCO_SOP_MRST 0x02000000 +#define R92C_APS_FSMCO_SOP_FUSE 0x04000000 +#define R92C_APS_FSMCO_SOP_ABG 0x08000000 +#define R92C_APS_FSMCO_SOP_AMB 0x10000000 +#define R92C_APS_FSMCO_SOP_RCK 0x20000000 +#define R92C_APS_FSMCO_SOP_A8M 0x40000000 +#define R92C_APS_FSMCO_XOP_BTCK 0x80000000 + +/* Bits for R92C_SYS_CLKR. */ +#define R92C_SYS_CLKR_ANAD16V_EN 0x00000001 +#define R92C_SYS_CLKR_ANA8M 0x00000002 +#define R92C_SYS_CLKR_MACSLP 0x00000010 +#define R92C_SYS_CLKR_LOADER_EN 0x00000020 +#define R92C_SYS_CLKR_80M_SSC_DIS 0x00000080 +#define R92C_SYS_CLKR_80M_SSC_EN_HO 0x00000100 +#define R92C_SYS_CLKR_PHY_SSC_RSTB 0x00000200 +#define R92C_SYS_CLKR_SEC_EN 0x00000400 +#define R92C_SYS_CLKR_MAC_EN 0x00000800 +#define R92C_SYS_CLKR_SYS_EN 0x00001000 +#define R92C_SYS_CLKR_RING_EN 0x00002000 + +/* Bits for R92C_RF_CTRL. */ +#define R92C_RF_CTRL_EN 0x01 +#define R92C_RF_CTRL_RSTB 0x02 +#define R92C_RF_CTRL_SDMRSTB 0x04 + +/* Bits for R92C_LDOV12D_CTRL. */ +#define R92C_LDOV12D_CTRL_LDV12_EN 0x01 + +/* Bits for R92C_EFUSE_CTRL. */ +#define R92C_EFUSE_CTRL_DATA_M 0x000000ff +#define R92C_EFUSE_CTRL_DATA_S 0 +#define R92C_EFUSE_CTRL_ADDR_M 0x0003ff00 +#define R92C_EFUSE_CTRL_ADDR_S 8 +#define R92C_EFUSE_CTRL_VALID 0x80000000 + +/* Bits for R92C_GPIO_MUXCFG. */ +#define R92C_GPIO_MUXCFG_RFKILL 0x0008 +#define R92C_GPIO_MUXCFG_ENBT 0x0020 + +/* Bits for R92C_GPIO_IO_SEL. */ +#define R92C_GPIO_IO_SEL_RFKILL 0x0008 + +/* Bits for R92C_LEDCFG0. */ +#define R92C_LEDCFG0_DIS 0x08 + +/* Bits for R92C_LEDCFG2. */ +#define R92C_LEDCFG2_EN 0x60 +#define R92C_LEDCFG2_DIS 0x68 + +/* Bits for R92C_MCUFWDL. */ +#define R92C_MCUFWDL_EN 0x00000001 +#define R92C_MCUFWDL_RDY 0x00000002 +#define R92C_MCUFWDL_CHKSUM_RPT 0x00000004 +#define R92C_MCUFWDL_MACINI_RDY 0x00000008 +#define R92C_MCUFWDL_BBINI_RDY 0x00000010 +#define R92C_MCUFWDL_RFINI_RDY 0x00000020 +#define R92C_MCUFWDL_WINTINI_RDY 0x00000040 +#define R92C_MCUFWDL_RAM_DL_SEL 0x00000080 /* 1: RAM, 0: ROM */ +#define R92C_MCUFWDL_PAGE_M 0x00070000 +#define R92C_MCUFWDL_PAGE_S 16 +#define R92C_MCUFWDL_CPRST 0x00800000 + +/* Bits for R92C_HPON_FSM. */ +#define R92C_HPON_FSM_CHIP_BONDING_ID_S 22 +#define R92C_HPON_FSM_CHIP_BONDING_ID_M 0x00c00000 +#define R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R 1 + +/* Bits for R92C_SYS_CFG. */ +#define R92C_SYS_CFG_XCLK_VLD 0x00000001 +#define R92C_SYS_CFG_ACLK_VLD 0x00000002 +#define R92C_SYS_CFG_UCLK_VLD 0x00000004 +#define R92C_SYS_CFG_PCLK_VLD 0x00000008 +#define R92C_SYS_CFG_PCIRSTB 0x00000010 +#define R92C_SYS_CFG_V15_VLD 0x00000020 +#define R92C_SYS_CFG_TRP_B15V_EN 0x00000080 +#define R92C_SYS_CFG_SIC_IDLE 0x00000100 +#define R92C_SYS_CFG_BD_MAC2 0x00000200 +#define R92C_SYS_CFG_BD_MAC1 0x00000400 +#define R92C_SYS_CFG_IC_MACPHY_MODE 0x00000800 +#define R92C_SYS_CFG_CHIP_VER_RTL_M 0x0000f000 +#define R92C_SYS_CFG_CHIP_VER_RTL_S 12 +#define R92C_SYS_CFG_BT_FUNC 0x00010000 +#define R92C_SYS_CFG_VENDOR_UMC 0x00080000 +#define R92C_SYS_CFG_PAD_HWPD_IDN 0x00400000 +#define R92C_SYS_CFG_TRP_VAUX_EN 0x00800000 +#define R92C_SYS_CFG_TRP_BT_EN 0x01000000 +#define R92C_SYS_CFG_BD_PKG_SEL 0x02000000 +#define R92C_SYS_CFG_BD_HCI_SEL 0x04000000 +#define R92C_SYS_CFG_TYPE_92C 0x08000000 + +/* Bits for R92C_CR. */ +#define R92C_CR_HCI_TXDMA_EN 0x00000001 +#define R92C_CR_HCI_RXDMA_EN 0x00000002 +#define R92C_CR_TXDMA_EN 0x00000004 +#define R92C_CR_RXDMA_EN 0x00000008 +#define R92C_CR_PROTOCOL_EN 0x00000010 +#define R92C_CR_SCHEDULE_EN 0x00000020 +#define R92C_CR_MACTXEN 0x00000040 +#define R92C_CR_MACRXEN 0x00000080 +#define R92C_CR_ENSEC 0x00000200 +#define R92C_CR_NETTYPE_S 16 +#define R92C_CR_NETTYPE_M 0x00030000 +#define R92C_CR_NETTYPE_NOLINK 0 +#define R92C_CR_NETTYPE_ADHOC 1 +#define R92C_CR_NETTYPE_INFRA 2 +#define R92C_CR_NETTYPE_AP 3 + +/* Bits for R92C_PBP. */ +#define R92C_PBP_PSRX_M 0x0f +#define R92C_PBP_PSRX_S 0 +#define R92C_PBP_PSTX_M 0xf0 +#define R92C_PBP_PSTX_S 4 +#define R92C_PBP_64 0 +#define R92C_PBP_128 1 +#define R92C_PBP_256 2 +#define R92C_PBP_512 3 +#define R92C_PBP_1024 4 + +/* Bits for R92C_TRXDMA_CTRL. */ +#define R92C_TRXDMA_CTRL_RXDMA_AGG_EN 0x0004 +#define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_M 0x0030 +#define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_S 4 +#define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_M 0x00c0 +#define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_S 6 +#define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_M 0x0300 +#define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_S 8 +#define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_M 0x0c00 +#define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_S 10 +#define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_M 0x3000 +#define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_S 12 +#define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_M 0xc000 +#define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_S 14 +#define R92C_TRXDMA_CTRL_QUEUE_LOW 1 +#define R92C_TRXDMA_CTRL_QUEUE_NORMAL 2 +#define R92C_TRXDMA_CTRL_QUEUE_HIGH 3 +#define R92C_TRXDMA_CTRL_QMAP_M 0xfff0 +#define R92C_TRXDMA_CTRL_QMAP_S 4 +/* Shortcuts. */ +#define R92C_TRXDMA_CTRL_QMAP_3EP 0xf5b0 +#define R92C_TRXDMA_CTRL_QMAP_HQ_LQ 0xf5f0 +#define R92C_TRXDMA_CTRL_QMAP_HQ_NQ 0xfaf0 +#define R92C_TRXDMA_CTRL_QMAP_LQ 0x5550 +#define R92C_TRXDMA_CTRL_QMAP_NQ 0xaaa0 +#define R92C_TRXDMA_CTRL_QMAP_HQ 0xfff0 + +/* Bits for R92C_LLT_INIT. */ +#define R92C_LLT_INIT_DATA_M 0x000000ff +#define R92C_LLT_INIT_DATA_S 0 +#define R92C_LLT_INIT_ADDR_M 0x0000ff00 +#define R92C_LLT_INIT_ADDR_S 8 +#define R92C_LLT_INIT_OP_M 0xc0000000 +#define R92C_LLT_INIT_OP_S 30 +#define R92C_LLT_INIT_OP_NO_ACTIVE 0 +#define R92C_LLT_INIT_OP_WRITE 1 + +/* Bits for R92C_RQPN. */ +#define R92C_RQPN_HPQ_M 0x000000ff +#define R92C_RQPN_HPQ_S 0 +#define R92C_RQPN_LPQ_M 0x0000ff00 +#define R92C_RQPN_LPQ_S 8 +#define R92C_RQPN_PUBQ_M 0x00ff0000 +#define R92C_RQPN_PUBQ_S 16 +#define R92C_RQPN_LD 0x80000000 + +/* Bits for R92C_TDECTRL. */ +#define R92C_TDECTRL_BLK_DESC_NUM_M 0x0000000f +#define R92C_TDECTRL_BLK_DESC_NUM_S 4 + +/* Bits for R92C_FWHW_TXQ_CTRL. */ +#define R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW 0x80 + +/* Bits for R92C_SPEC_SIFS. */ +#define R92C_SPEC_SIFS_CCK_M 0x00ff +#define R92C_SPEC_SIFS_CCK_S 0 +#define R92C_SPEC_SIFS_OFDM_M 0xff00 +#define R92C_SPEC_SIFS_OFDM_S 8 + +/* Bits for R92C_RL. */ +#define R92C_RL_LRL_M 0x003f +#define R92C_RL_LRL_S 0 +#define R92C_RL_SRL_M 0x3f00 +#define R92C_RL_SRL_S 8 + +/* Bits for R92C_RRSR. */ +#define R92C_RRSR_RATE_BITMAP_M 0x000fffff +#define R92C_RRSR_RATE_BITMAP_S 0 +#define R92C_RRSR_RATE_CCK_ONLY_1M 0xffff1 +#define R92C_RRSR_RATE_ALL 0xfffff +#define R92C_RRSR_RSC_LOWSUBCHNL 0x00200000 +#define R92C_RRSR_RSC_UPSUBCHNL 0x00400000 +#define R92C_RRSR_SHORT 0x00800000 + +/* Bits for R92C_EDCA_XX_PARAM. */ +#define R92C_EDCA_PARAM_AIFS_M 0x000000ff +#define R92C_EDCA_PARAM_AIFS_S 0 +#define R92C_EDCA_PARAM_ECWMIN_M 0x00000f00 +#define R92C_EDCA_PARAM_ECWMIN_S 8 +#define R92C_EDCA_PARAM_ECWMAX_M 0x0000f000 +#define R92C_EDCA_PARAM_ECWMAX_S 12 +#define R92C_EDCA_PARAM_TXOP_M 0xffff0000 +#define R92C_EDCA_PARAM_TXOP_S 16 + +/* Bits for R92C_TXPAUSE. */ +#define R92C_TXPAUSE_AC_VO 0x01 +#define R92C_TXPAUSE_AC_VI 0x02 +#define R92C_TXPAUSE_AC_BE 0x04 +#define R92C_TXPAUSE_AC_BK 0x08 + +/* Bits for R92C_BCN_CTRL. */ +#define R92C_BCN_CTRL_EN_MBSSID 0x02 +#define R92C_BCN_CTRL_TXBCN_RPT 0x04 +#define R92C_BCN_CTRL_EN_BCN 0x08 +#define R92C_BCN_CTRL_DIS_TSF_UDT0 0x10 + +/* Bits for R92C_APSD_CTRL. */ +#define R92C_APSD_CTRL_OFF 0x40 +#define R92C_APSD_CTRL_OFF_STATUS 0x80 + +/* Bits for R92C_BWOPMODE. */ +#define R92C_BWOPMODE_11J 0x01 +#define R92C_BWOPMODE_5G 0x02 +#define R92C_BWOPMODE_20MHZ 0x04 + +/* Bits for R92C_TCR. */ +#define R92C_TCR_TSFRST 0x00000001 +#define R92C_TCR_DIS_GCLK 0x00000002 +#define R92C_TCR_PAD_SEL 0x00000004 +#define R92C_TCR_PWR_ST 0x00000040 +#define R92C_TCR_PWRBIT_OW_EN 0x00000080 +#define R92C_TCR_ACRC 0x00000100 +#define R92C_TCR_CFENDFORM 0x00000200 +#define R92C_TCR_ICV 0x00000400 + +/* Bits for R92C_RCR. */ +#define R92C_RCR_AAP 0x00000001 +#define R92C_RCR_APM 0x00000002 +#define R92C_RCR_AM 0x00000004 +#define R92C_RCR_AB 0x00000008 +#define R92C_RCR_ADD3 0x00000010 +#define R92C_RCR_APWRMGT 0x00000020 +#define R92C_RCR_CBSSID_DATA 0x00000040 +#define R92C_RCR_CBSSID_BCN 0x00000080 +#define R92C_RCR_ACRC32 0x00000100 +#define R92C_RCR_AICV 0x00000200 +#define R92C_RCR_ADF 0x00000800 +#define R92C_RCR_ACF 0x00001000 +#define R92C_RCR_AMF 0x00002000 +#define R92C_RCR_HTC_LOC_CTRL 0x00004000 +#define R92C_RCR_MFBEN 0x00400000 +#define R92C_RCR_LSIGEN 0x00800000 +#define R92C_RCR_ENMBID 0x01000000 +#define R92C_RCR_APP_BA_SSN 0x08000000 +#define R92C_RCR_APP_PHYSTS 0x10000000 +#define R92C_RCR_APP_ICV 0x20000000 +#define R92C_RCR_APP_MIC 0x40000000 +#define R92C_RCR_APPFCS 0x80000000 + +/* Bits for R92C_CAMCMD. */ +#define R92C_CAMCMD_ADDR_M 0x0000ffff +#define R92C_CAMCMD_ADDR_S 0 +#define R92C_CAMCMD_WRITE 0x00010000 +#define R92C_CAMCMD_CLR 0x40000000 +#define R92C_CAMCMD_POLLING 0x80000000 + +/* IMR */ + +/*Beacon DMA interrupt 6 */ +#define R92C_IMR_BCNDMAINT6 0x80000000 +/*Beacon DMA interrupt 5 */ +#define R92C_IMR_BCNDMAINT5 0x40000000 +/*Beacon DMA interrupt 4 */ +#define R92C_IMR_BCNDMAINT4 0x20000000 +/*Beacon DMA interrupt 3 */ +#define R92C_IMR_BCNDMAINT3 0x10000000 +/*Beacon DMA interrupt 2 */ +#define R92C_IMR_BCNDMAINT2 0x08000000 +/*Beacon DMA interrupt 1 */ +#define R92C_IMR_BCNDMAINT1 0x04000000 +/*Beacon Queue DMA OK interrupt 8 */ +#define R92C_IMR_BCNDOK8 0x02000000 +/*Beacon Queue DMA OK interrupt 7 */ +#define R92C_IMR_BCNDOK7 0x01000000 +/*Beacon Queue DMA OK interrupt 6 */ +#define R92C_IMR_BCNDOK6 0x00800000 +/*Beacon Queue DMA OK interrupt 5 */ +#define R92C_IMR_BCNDOK5 0x00400000 +/*Beacon Queue DMA OK interrupt 4 */ +#define R92C_IMR_BCNDOK4 0x00200000 +/*Beacon Queue DMA OK interrupt 3 */ +#define R92C_IMR_BCNDOK3 0x00100000 +/*Beacon Queue DMA OK interrupt 2 */ +#define R92C_IMR_BCNDOK2 0x00080000 +/*Beacon Queue DMA OK interrupt 1 */ +#define R92C_IMR_BCNDOK1 0x00040000 +/*Timeout interrupt 2 */ +#define R92C_IMR_TIMEOUT2 0x00020000 +/*Timeout interrupt 1 */ +#define R92C_IMR_TIMEOUT1 0x00010000 +/*Transmit FIFO Overflow */ +#define R92C_IMR_TXFOVW 0x00008000 +/*Power save time out interrupt */ +#define R92C_IMR_PSTIMEOUT 0x00004000 +/*Beacon DMA interrupt 0 */ +#define R92C_IMR_BCNINT 0x00002000 +/*Receive FIFO Overflow */ +#define R92C_IMR_RXFOVW 0x00001000 +/*Receive Descriptor Unavailable */ +#define R92C_IMR_RDU 0x00000800 +/*For 92C,ATIM Window End interrupt */ +#define R92C_IMR_ATIMEND 0x00000400 +/*Beacon Queue DMA OK interrupt */ +#define R92C_IMR_BDOK 0x00000200 +/*High Queue DMA OK interrupt */ +#define R92C_IMR_HIGHDOK 0x00000100 +/*Transmit Beacon OK interrupt */ +#define R92C_IMR_TBDOK 0x00000080 +/*Management Queue DMA OK interrupt */ +#define R92C_IMR_MGNTDOK 0x00000040 +/*For 92C,Transmit Beacon Error interrupt */ +#define R92C_IMR_TBDER 0x00000020 +/*AC_BK DMA OK interrupt */ +#define R92C_IMR_BKDOK 0x00000010 +/*AC_BE DMA OK interrupt */ +#define R92C_IMR_BEDOK 0x00000008 +/*AC_VI DMA OK interrupt */ +#define R92C_IMR_VIDOK 0x00000004 +/*AC_VO DMA interrupt */ +#define R92C_IMR_VODOK 0x00000002 +/*Receive DMA OK interrupt */ +#define R92C_IMR_ROK 0x00000001 + +#define R92C_IBSS_INT_MASK (R92C_IMR_BCNINT | R92C_IMR_TBDOK | R92C_IMR_TBDER) + +/* + * Baseband registers. + */ +#define R92C_FPGA0_RFMOD 0x800 +#define R92C_FPGA0_TXINFO 0x804 +#define R92C_HSSI_PARAM1(chain) (0x820 + (chain) * 8) +#define R92C_HSSI_PARAM2(chain) (0x824 + (chain) * 8) +#define R92C_TXAGC_RATE18_06(i) (((i) == 0) ? 0xe00 : 0x830) +#define R92C_TXAGC_RATE54_24(i) (((i) == 0) ? 0xe04 : 0x834) +#define R92C_TXAGC_A_CCK1_MCS32 0xe08 +#define R92C_TXAGC_B_CCK1_55_MCS32 0x838 +#define R92C_TXAGC_B_CCK11_A_CCK2_11 0x86c +#define R92C_TXAGC_MCS03_MCS00(i) (((i) == 0) ? 0xe10 : 0x83c) +#define R92C_TXAGC_MCS07_MCS04(i) (((i) == 0) ? 0xe14 : 0x848) +#define R92C_TXAGC_MCS11_MCS08(i) (((i) == 0) ? 0xe18 : 0x84c) +#define R92C_TXAGC_MCS15_MCS12(i) (((i) == 0) ? 0xe1c : 0x868) +#define R92C_LSSI_PARAM(chain) (0x840 + (chain) * 4) +#define R92C_FPGA0_RFIFACEOE(chain) (0x860 + (chain) * 4) +#define R92C_FPGA0_RFIFACESW(idx) (0x870 + (idx) * 4) +#define R92C_FPGA0_RFPARAM(idx) (0x878 + (idx) * 4) +#define R92C_FPGA0_ANAPARAM2 0x884 +#define R92C_LSSI_READBACK(chain) (0x8a0 + (chain) * 4) +#define R92C_HSPI_READBACK(chain) (0x8b8 + (chain) * 4) +#define R92C_FPGA1_RFMOD 0x900 +#define R92C_FPGA1_TXINFO 0x90c +#define R92C_CCK0_SYSTEM 0xa00 +#define R92C_CCK0_AFESETTING 0xa04 +#define R92C_OFDM0_TRXPATHENA 0xc04 +#define R92C_OFDM0_TRMUXPAR 0xc08 +#define R92C_OFDM0_RXIQIMBALANCE(chain) (0xc14 + (chain) * 8) +#define R92C_OFDM0_ECCATHRESHOLD 0xc4c +#define R92C_OFDM0_AGCCORE1(chain) (0xc50 + (chain) * 8) +#define R92C_OFDM0_AGCPARAM1 0xc70 +#define R92C_OFDM0_AGCRSSITABLE 0xc78 +#define R92C_OFDM0_TXIQIMBALANCE(chain) (0xc80 + (chain) * 8) +#define R92C_OFDM0_TXAFE(chain) (0xc94 + (chain) * 8) +#define R92C_OFDM0_RXIQEXTANTA 0xca0 +#define R92C_OFDM1_LSTF 0xd00 + +/* Bits for R92C_FPGA[01]_RFMOD. */ +#define R92C_RFMOD_40MHZ 0x00000001 +#define R92C_RFMOD_JAPAN 0x00000002 +#define R92C_RFMOD_CCK_TXSC 0x00000030 +#define R92C_RFMOD_CCK_EN 0x01000000 +#define R92C_RFMOD_OFDM_EN 0x02000000 + +/* Bits for R92C_HSSI_PARAM1(i). */ +#define R92C_HSSI_PARAM1_PI 0x00000100 + +/* Bits for R92C_HSSI_PARAM2(i). */ +#define R92C_HSSI_PARAM2_CCK_HIPWR 0x00000200 +#define R92C_HSSI_PARAM2_ADDR_LENGTH 0x00000400 +#define R92C_HSSI_PARAM2_DATA_LENGTH 0x00000800 +#define R92C_HSSI_PARAM2_READ_ADDR_M 0x7f800000 +#define R92C_HSSI_PARAM2_READ_ADDR_S 23 +#define R92C_HSSI_PARAM2_READ_EDGE 0x80000000 + +/* Bits for R92C_TXAGC_A_CCK1_MCS32. */ +#define R92C_TXAGC_A_CCK1_M 0x0000ff00 +#define R92C_TXAGC_A_CCK1_S 8 + +/* Bits for R92C_TXAGC_B_CCK11_A_CCK2_11. */ +#define R92C_TXAGC_B_CCK11_M 0x000000ff +#define R92C_TXAGC_B_CCK11_S 0 +#define R92C_TXAGC_A_CCK2_M 0x0000ff00 +#define R92C_TXAGC_A_CCK2_S 8 +#define R92C_TXAGC_A_CCK55_M 0x00ff0000 +#define R92C_TXAGC_A_CCK55_S 16 +#define R92C_TXAGC_A_CCK11_M 0xff000000 +#define R92C_TXAGC_A_CCK11_S 24 + +/* Bits for R92C_TXAGC_B_CCK1_55_MCS32. */ +#define R92C_TXAGC_B_CCK1_M 0x0000ff00 +#define R92C_TXAGC_B_CCK1_S 8 +#define R92C_TXAGC_B_CCK2_M 0x00ff0000 +#define R92C_TXAGC_B_CCK2_S 16 +#define R92C_TXAGC_B_CCK55_M 0xff000000 +#define R92C_TXAGC_B_CCK55_S 24 + +/* Bits for R92C_TXAGC_RATE18_06(x). */ +#define R92C_TXAGC_RATE06_M 0x000000ff +#define R92C_TXAGC_RATE06_S 0 +#define R92C_TXAGC_RATE09_M 0x0000ff00 +#define R92C_TXAGC_RATE09_S 8 +#define R92C_TXAGC_RATE12_M 0x00ff0000 +#define R92C_TXAGC_RATE12_S 16 +#define R92C_TXAGC_RATE18_M 0xff000000 +#define R92C_TXAGC_RATE18_S 24 + +/* Bits for R92C_TXAGC_RATE54_24(x). */ +#define R92C_TXAGC_RATE24_M 0x000000ff +#define R92C_TXAGC_RATE24_S 0 +#define R92C_TXAGC_RATE36_M 0x0000ff00 +#define R92C_TXAGC_RATE36_S 8 +#define R92C_TXAGC_RATE48_M 0x00ff0000 +#define R92C_TXAGC_RATE48_S 16 +#define R92C_TXAGC_RATE54_M 0xff000000 +#define R92C_TXAGC_RATE54_S 24 + +/* Bits for R92C_TXAGC_MCS03_MCS00(x). */ +#define R92C_TXAGC_MCS00_M 0x000000ff +#define R92C_TXAGC_MCS00_S 0 +#define R92C_TXAGC_MCS01_M 0x0000ff00 +#define R92C_TXAGC_MCS01_S 8 +#define R92C_TXAGC_MCS02_M 0x00ff0000 +#define R92C_TXAGC_MCS02_S 16 +#define R92C_TXAGC_MCS03_M 0xff000000 +#define R92C_TXAGC_MCS03_S 24 + +/* Bits for R92C_TXAGC_MCS07_MCS04(x). */ +#define R92C_TXAGC_MCS04_M 0x000000ff +#define R92C_TXAGC_MCS04_S 0 +#define R92C_TXAGC_MCS05_M 0x0000ff00 +#define R92C_TXAGC_MCS05_S 8 +#define R92C_TXAGC_MCS06_M 0x00ff0000 +#define R92C_TXAGC_MCS06_S 16 +#define R92C_TXAGC_MCS07_M 0xff000000 +#define R92C_TXAGC_MCS07_S 24 + +/* Bits for R92C_TXAGC_MCS11_MCS08(x). */ +#define R92C_TXAGC_MCS08_M 0x000000ff +#define R92C_TXAGC_MCS08_S 0 +#define R92C_TXAGC_MCS09_M 0x0000ff00 +#define R92C_TXAGC_MCS09_S 8 +#define R92C_TXAGC_MCS10_M 0x00ff0000 +#define R92C_TXAGC_MCS10_S 16 +#define R92C_TXAGC_MCS11_M 0xff000000 +#define R92C_TXAGC_MCS11_S 24 + +/* Bits for R92C_TXAGC_MCS15_MCS12(x). */ +#define R92C_TXAGC_MCS12_M 0x000000ff +#define R92C_TXAGC_MCS12_S 0 +#define R92C_TXAGC_MCS13_M 0x0000ff00 +#define R92C_TXAGC_MCS13_S 8 +#define R92C_TXAGC_MCS14_M 0x00ff0000 +#define R92C_TXAGC_MCS14_S 16 +#define R92C_TXAGC_MCS15_M 0xff000000 +#define R92C_TXAGC_MCS15_S 24 + +/* Bits for R92C_LSSI_PARAM(i). */ +#define R92C_LSSI_PARAM_DATA_M 0x000fffff +#define R92C_LSSI_PARAM_DATA_S 0 +#define R92C_LSSI_PARAM_ADDR_M 0x03f00000 +#define R92C_LSSI_PARAM_ADDR_S 20 + +/* Bits for R92C_FPGA0_ANAPARAM2. */ +#define R92C_FPGA0_ANAPARAM2_CBW20 0x00000400 + +/* Bits for R92C_LSSI_READBACK(i). */ +#define R92C_LSSI_READBACK_DATA_M 0x000fffff +#define R92C_LSSI_READBACK_DATA_S 0 + +/* Bits for R92C_OFDM0_AGCCORE1(i). */ +#define R92C_OFDM0_AGCCORE1_GAIN_M 0x0000007f +#define R92C_OFDM0_AGCCORE1_GAIN_S 0 + + +/* + * USB registers. + */ +#define R92C_USB_INFO 0xfe17 +#define R92C_USB_SPECIAL_OPTION 0xfe55 +#define R92C_USB_HCPWM 0xfe57 +#define R92C_USB_HRPWM 0xfe58 +#define R92C_USB_DMA_AGG_TO 0xfe5b +#define R92C_USB_AGG_TO 0xfe5c +#define R92C_USB_AGG_TH 0xfe5d +#define R92C_USB_VID 0xfe60 +#define R92C_USB_PID 0xfe62 +#define R92C_USB_OPTIONAL 0xfe64 +#define R92C_USB_EP 0xfe65 +#define R92C_USB_PHY 0xfe68 +#define R92C_USB_MAC_ADDR 0xfe70 +#define R92C_USB_STRING 0xfe80 + +/* Bits for R92C_USB_SPECIAL_OPTION. */ +#define R92C_USB_SPECIAL_OPTION_AGG_EN 0x08 + +/* Bits for R92C_USB_EP. */ +#define R92C_USB_EP_HQ_M 0x000f +#define R92C_USB_EP_HQ_S 0 +#define R92C_USB_EP_NQ_M 0x00f0 +#define R92C_USB_EP_NQ_S 4 +#define R92C_USB_EP_LQ_M 0x0f00 +#define R92C_USB_EP_LQ_S 8 + + +/* + * Firmware base address. + */ +#define R92C_FW_START_ADDR 0x1000 +#define R92C_FW_PAGE_SIZE 4096 + + +/* + * RF (6052) registers. + */ +#define R92C_RF_AC 0x00 +#define R92C_RF_IQADJ_G(i) (0x01 + (i)) +#define R92C_RF_POW_TRSW 0x05 +#define R92C_RF_GAIN_RX 0x06 +#define R92C_RF_GAIN_TX 0x07 +#define R92C_RF_TXM_IDAC 0x08 +#define R92C_RF_BS_IQGEN 0x0f +#define R92C_RF_MODE1 0x10 +#define R92C_RF_MODE2 0x11 +#define R92C_RF_RX_AGC_HP 0x12 +#define R92C_RF_TX_AGC 0x13 +#define R92C_RF_BIAS 0x14 +#define R92C_RF_IPA 0x15 +#define R92C_RF_POW_ABILITY 0x17 +#define R92C_RF_CHNLBW 0x18 +#define R92C_RF_RX_G1 0x1a +#define R92C_RF_RX_G2 0x1b +#define R92C_RF_RX_BB2 0x1c +#define R92C_RF_RX_BB1 0x1d +#define R92C_RF_RCK1 0x1e +#define R92C_RF_RCK2 0x1f +#define R92C_RF_TX_G(i) (0x20 + (i)) +#define R92C_RF_TX_BB1 0x23 +#define R92C_RF_T_METER 0x24 +#define R92C_RF_SYN_G(i) (0x25 + (i)) +#define R92C_RF_RCK_OS 0x30 +#define R92C_RF_TXPA_G(i) (0x31 + (i)) + +/* Bits for R92C_RF_AC. */ +#define R92C_RF_AC_MODE_M 0x70000 +#define R92C_RF_AC_MODE_S 16 +#define R92C_RF_AC_MODE_STANDBY 1 + +/* Bits for R92C_RF_CHNLBW. */ +#define R92C_RF_CHNLBW_CHNL_M 0x003ff +#define R92C_RF_CHNLBW_CHNL_S 0 +#define R92C_RF_CHNLBW_BW20 0x00400 +#define R92C_RF_CHNLBW_LCSTART 0x08000 + + +/* + * CAM entries. + */ +#define R92C_CAM_ENTRY_COUNT 32 + +#define R92C_CAM_CTL0(entry) ((entry) * 8 + 0) +#define R92C_CAM_CTL1(entry) ((entry) * 8 + 1) +#define R92C_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i)) + +/* Bits for R92C_CAM_CTL0(i). */ +#define R92C_CAM_KEYID_M 0x00000003 +#define R92C_CAM_KEYID_S 0 +#define R92C_CAM_ALGO_M 0x0000001c +#define R92C_CAM_ALGO_S 2 +#define R92C_CAM_ALGO_NONE 0 +#define R92C_CAM_ALGO_WEP40 1 +#define R92C_CAM_ALGO_TKIP 2 +#define R92C_CAM_ALGO_AES 4 +#define R92C_CAM_ALGO_WEP104 5 +#define R92C_CAM_VALID 0x00008000 +#define R92C_CAM_MACLO_M 0xffff0000 +#define R92C_CAM_MACLO_S 16 + +/* Rate adaptation modes. */ +#define R92C_RAID_11GN 1 +#define R92C_RAID_11N 3 +#define R92C_RAID_11BG 4 +#define R92C_RAID_11G 5 /* "pure" 11g */ +#define R92C_RAID_11B 6 + + +/* Macros to access unaligned little-endian memory. */ +#define LE_READ_2(x) ((x)[0] | (x)[1] << 8) +#define LE_READ_4(x) ((x)[0] | (x)[1] << 8 | (x)[2] << 16 | (x)[3] << 24) + +/* + * Macros to access subfields in registers. + */ +/* Mask and Shift (getter). */ +#define MS(val, field) \ + (((val) & field##_M) >> field##_S) + +/* Shift and Mask (setter). */ +#define SM(field, val) \ + (((val) << field##_S) & field##_M) + +/* Rewrite. */ +#define RW(var, field, val) \ + (((var) & ~field##_M) | SM(field, val)) + +/* + * Firmware image header. + */ +struct r92c_fw_hdr { + /* QWORD0 */ + uint16_t signature; + uint8_t category; + uint8_t function; + uint16_t version; + uint16_t subversion; + /* QWORD1 */ + uint8_t month; + uint8_t date; + uint8_t hour; + uint8_t minute; + uint16_t ramcodesize; + uint16_t reserved2; + /* QWORD2 */ + uint32_t svnidx; + uint32_t reserved3; + /* QWORD3 */ + uint32_t reserved4; + uint32_t reserved5; +} __packed; + +/* + * Host to firmware commands. + */ +struct r92c_fw_cmd { + uint8_t id; +#define R92C_CMD_AP_OFFLOAD 0 +#define R92C_CMD_SET_PWRMODE 1 +#define R92C_CMD_JOINBSS_RPT 2 +#define R92C_CMD_RSVD_PAGE 3 +#define R92C_CMD_RSSI 4 +#define R92C_CMD_RSSI_SETTING 5 +#define R92C_CMD_MACID_CONFIG 6 +#define R92C_CMD_MACID_PS_MODE 7 +#define R92C_CMD_P2P_PS_OFFLOAD 8 +#define R92C_CMD_SELECTIVE_SUSPEND 9 +#define R92C_CMD_FLAG_EXT 0x80 + + uint8_t msg[5]; +} __packed; + +/* Structure for R92C_CMD_RSSI_SETTING. */ +struct r92c_fw_cmd_rssi { + uint8_t macid; + uint8_t reserved; + uint8_t pwdb; +} __packed; + +/* Structure for R92C_CMD_MACID_CONFIG. */ +struct r92c_fw_cmd_macid_cfg { + uint32_t mask; + uint8_t macid; +#define RTWN_MACID_BSS 0 +#define RTWN_MACID_BC 4 /* Broadcast. */ +#define RTWN_MACID_VALID 0x80 +} __packed; + +/* + * RTL8192CU ROM image. + */ +struct r92c_rom { + uint16_t id; /* 0x8129 */ + uint8_t reserved1[5]; + uint8_t dbg_sel; + uint16_t reserved2; + uint16_t vid; + uint16_t pid; + uint8_t usb_opt; + uint8_t ep_setting; + uint16_t reserved3; + uint8_t usb_phy; + uint8_t reserved4[3]; + uint8_t macaddr[6]; + uint8_t string[61]; /* "Realtek" */ + uint8_t subcustomer_id; + uint8_t cck_tx_pwr[R92C_MAX_CHAINS][3]; + uint8_t ht40_1s_tx_pwr[R92C_MAX_CHAINS][3]; + uint8_t ht40_2s_tx_pwr_diff[3]; + uint8_t ht20_tx_pwr_diff[3]; + uint8_t ofdm_tx_pwr_diff[3]; + uint8_t ht40_max_pwr[3]; + uint8_t ht20_max_pwr[3]; + uint8_t xtal_calib; + uint8_t tssi[R92C_MAX_CHAINS]; + uint8_t thermal_meter; + uint8_t rf_opt1; +#define R92C_ROM_RF1_REGULATORY_M 0x07 +#define R92C_ROM_RF1_REGULATORY_S 0 +#define R92C_ROM_RF1_BOARD_TYPE_M 0xe0 +#define R92C_ROM_RF1_BOARD_TYPE_S 5 +#define R92C_BOARD_TYPE_DONGLE 0 +#define R92C_BOARD_TYPE_HIGHPA 1 +#define R92C_BOARD_TYPE_MINICARD 2 +#define R92C_BOARD_TYPE_SOLO 3 +#define R92C_BOARD_TYPE_COMBO 4 + + uint8_t rf_opt2; + uint8_t rf_opt3; + uint8_t rf_opt4; + uint8_t channel_plan; + uint8_t version; + uint8_t curstomer_id; +} __packed; + +/* Rx MAC descriptor. */ +struct r92c_rx_desc { + uint32_t rxdw0; +#define R92C_RXDW0_PKTLEN_M 0x00003fff +#define R92C_RXDW0_PKTLEN_S 0 +#define R92C_RXDW0_CRCERR 0x00004000 +#define R92C_RXDW0_ICVERR 0x00008000 +#define R92C_RXDW0_INFOSZ_M 0x000f0000 +#define R92C_RXDW0_INFOSZ_S 16 +#define R92C_RXDW0_QOS 0x00800000 +#define R92C_RXDW0_SHIFT_M 0x03000000 +#define R92C_RXDW0_SHIFT_S 24 +#define R92C_RXDW0_PHYST 0x04000000 +#define R92C_RXDW0_DECRYPTED 0x08000000 +#define R92C_RXDW0_LS 0x10000000 +#define R92C_RXDW0_FS 0x20000000 +#define R92C_RXDW0_EOR 0x40000000 +#define R92C_RXDW0_OWN 0x80000000 + + uint32_t rxdw1; + uint32_t rxdw2; +#define R92C_RXDW2_PKTCNT_M 0x00ff0000 +#define R92C_RXDW2_PKTCNT_S 16 + + uint32_t rxdw3; +#define R92C_RXDW3_RATE_M 0x0000003f +#define R92C_RXDW3_RATE_S 0 +#define R92C_RXDW3_HT 0x00000040 +#define R92C_RXDW3_HTC 0x00000400 + + uint32_t rxdw4; + uint32_t rxdw5; + + uint32_t rxbufaddr; + uint32_t rxbufaddr64; +} __packed __attribute__((aligned(4))); + +/* Rx PHY descriptor. */ +struct r92c_rx_phystat { + uint32_t phydw0; + uint32_t phydw1; + uint32_t phydw2; + uint32_t phydw3; + uint32_t phydw4; + uint32_t phydw5; + uint32_t phydw6; + uint32_t phydw7; +} __packed __attribute__((aligned(4))); + +/* Rx PHY CCK descriptor. */ +struct r92c_rx_cck { + uint8_t adc_pwdb[4]; + uint8_t sq_rpt; + uint8_t agc_rpt; +} __packed; + +/* Tx MAC descriptor. */ +struct r92c_tx_desc { + uint32_t txdw0; +#define R92C_TXDW0_PKTLEN_M 0x0000ffff +#define R92C_TXDW0_PKTLEN_S 0 +#define R92C_TXDW0_OFFSET_M 0x00ff0000 +#define R92C_TXDW0_OFFSET_S 16 +#define R92C_TXDW0_BMCAST 0x01000000 +#define R92C_TXDW0_LSG 0x04000000 +#define R92C_TXDW0_FSG 0x08000000 +#define R92C_TXDW0_OWN 0x80000000 + + uint32_t txdw1; +#define R92C_TXDW1_MACID_M 0x0000001f +#define R92C_TXDW1_MACID_S 0 +#define R92C_TXDW1_AGGEN 0x00000020 +#define R92C_TXDW1_AGGBK 0x00000040 +#define R92C_TXDW1_QSEL_M 0x00001f00 +#define R92C_TXDW1_QSEL_S 8 +#define R92C_TXDW1_QSEL_BE 0x00 +#define R92C_TXDW1_QSEL_BK 0x02 +#define R92C_TXDW1_QSEL_VI 0x05 +#define R92C_TXDW1_QSEL_VO 0x07 +#define R92C_TXDW1_QSEL_BEACON 0x10 +#define R92C_TXDW1_QSEL_HIGH 0x11 +#define R92C_TXDW1_QSEL_MGNT 0x12 +#define R92C_TXDW1_QSEL_CMD 0x13 +#define R92C_TXDW1_RAID_M 0x000f0000 +#define R92C_TXDW1_RAID_S 16 +#define R92C_TXDW1_CIPHER_M 0x00c00000 +#define R92C_TXDW1_CIPHER_S 22 +#define R92C_TXDW1_CIPHER_NONE 0 +#define R92C_TXDW1_CIPHER_RC4 1 +#define R92C_TXDW1_CIPHER_AES 3 +#define R92C_TXDW1_PKTOFF_M 0x7c000000 +#define R92C_TXDW1_PKTOFF_S 26 + + uint32_t txdw2; + uint16_t txdw3; + uint16_t txdseq; + + uint32_t txdw4; +#define R92C_TXDW4_RTSRATE_M 0x0000003f +#define R92C_TXDW4_RTSRATE_S 0 +#define R92C_TXDW4_QOS 0x00000040 +#define R92C_TXDW4_HWSEQ 0x00000080 +#define R92C_TXDW4_DRVRATE 0x00000100 +#define R92C_TXDW4_CTS2SELF 0x00000800 +#define R92C_TXDW4_RTSEN 0x00001000 +#define R92C_TXDW4_HWRTSEN 0x00002000 +#define R92C_TXDW4_SCO_M 0x003f0000 +#define R92C_TXDW4_SCO_S 20 +#define R92C_TXDW4_SCO_SCA 1 +#define R92C_TXDW4_SCO_SCB 2 +#define R92C_TXDW4_40MHZ 0x02000000 + + uint32_t txdw5; +#define R92C_TXDW5_DATARATE_M 0x0000003f +#define R92C_TXDW5_DATARATE_S 0 +#define R92C_TXDW5_SGI 0x00000040 +#define R92C_TXDW5_DATARATE_FBLIMIT_M 0x00001f00 +#define R92C_TXDW5_DATARATE_FBLIMIT_S 8 +#define R92C_TXDW5_RTSRATE_FBLIMIT_M 0x0001e000 +#define R92C_TXDW5_RTSRATE_FBLIMIT_S 13 +#define R92C_TXDW5_RETRY_LIMIT_ENABLE 0x00020000 +#define R92C_TXDW5_DATA_RETRY_LIMIT_M 0x00fc0000 +#define R92C_TXDW5_DATA_RETRY_LIMIT_S 18 +#define R92C_TXDW5_AGGNUM_M 0xff000000 +#define R92C_TXDW5_AGGNUM_S 24 + + uint32_t txdw6; + + uint16_t txbufsize; + uint16_t pad; + + uint32_t txbufaddr; + uint32_t txbufaddr64; + + uint32_t nextdescaddr; + uint32_t nextdescaddr64; + + uint32_t reserved[4]; +} __packed __attribute__((aligned(4))); + + +/* + * Driver definitions. + */ +#define RTWN_NTXQUEUES 9 +#define RTWN_RX_LIST_COUNT 256 +#define RTWN_TX_LIST_COUNT 256 +#define RTWN_HOST_CMD_RING_COUNT 32 + +/* TX queue indices. */ +#define RTWN_BK_QUEUE 0 +#define RTWN_BE_QUEUE 1 +#define RTWN_VI_QUEUE 2 +#define RTWN_VO_QUEUE 3 +#define RTWN_BEACON_QUEUE 4 +#define RTWN_TXCMD_QUEUE 5 +#define RTWN_MGNT_QUEUE 6 +#define RTWN_HIGH_QUEUE 7 +#define RTWN_HCCA_QUEUE 8 + +/* RX queue indices. */ +#define RTWN_RX_QUEUE 0 + +#define RTWN_RXBUFSZ (16 * 1024) +#define RTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) + +#define RTWN_RIDX_COUNT 28 + +#define RTWN_TX_TIMEOUT 5000 /* ms */ + +#define RTWN_LED_LINK 0 +#define RTWN_LED_DATA 1 + +struct rtwn_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_dbm_antsignal; +} __packed; + +#define RTWN_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) + +struct rtwn_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define RTWN_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_CHANNEL) + +struct rtwn_softc; + +struct rtwn_rx_data { + bus_dmamap_t map; + struct mbuf *m; + bus_addr_t paddr; +}; + +struct rtwn_rx_ring { + struct r92c_rx_desc *desc; + bus_addr_t paddr; + bus_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_dma_tag_t data_dmat; + bus_dma_segment_t seg; + struct rtwn_rx_data rx_data[RTWN_RX_LIST_COUNT]; + +}; +struct rtwn_tx_data { + bus_dmamap_t map; + struct mbuf *m; + struct ieee80211_node *ni; +}; + +struct rtwn_tx_ring { + bus_addr_t paddr; + bus_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_dma_tag_t data_dmat; + bus_dma_segment_t seg; + struct r92c_tx_desc *desc; + struct rtwn_tx_data tx_data[RTWN_TX_LIST_COUNT]; + int queued; + int cur; +}; + +struct rtwn_host_cmd { + void (*cb)(struct rtwn_softc *, void *); + uint8_t data[256]; +}; + +struct rtwn_cmd_key { + struct ieee80211_key key; + uint16_t associd; +}; + +struct rtwn_host_cmd_ring { + struct rtwn_host_cmd cmd[RTWN_HOST_CMD_RING_COUNT]; + int cur; + int next; + int queued; +}; + +struct rtwn_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define RTWN_VAP(vap) ((struct rtwn_vap *)(vap)) + +struct rtwn_softc { + device_t sc_dev; + struct mtx sc_mtx; + struct ieee80211com sc_ic; + struct mbufq sc_snd; + + struct resource *irq; + struct resource *mem; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + void *sc_ih; + bus_size_t sc_mapsize; + int sc_cap_off; + + struct task sc_reinit_task; + struct callout calib_to; + struct callout watchdog_to; + + int sc_debug; + + u_int sc_flags; +#define RTWN_FLAG_CCK_HIPWR 0x01 +#define RTWN_FLAG_BUSY 0x02 +#define RTWN_RUNNING 0x04 + + u_int chip; +#define RTWN_CHIP_88C 0x00 +#define RTWN_CHIP_92C 0x01 +#define RTWN_CHIP_92C_1T2R 0x02 +#define RTWN_CHIP_UMC 0x04 +#define RTWN_CHIP_UMC_A_CUT 0x08 + + uint8_t board_type; + uint8_t regulatory; + uint8_t pa_setting; + int avg_pwdb; + int thcal_state; + int thcal_lctemp; + int ntxchains; + int nrxchains; + int ledlink; + + int sc_tx_timer; + int fwcur; + struct rtwn_rx_ring rx_ring; + struct rtwn_tx_ring tx_ring[RTWN_NTXQUEUES]; + uint32_t qfullmsk; + struct r92c_rom rom; + + uint32_t rf_chnlbw[R92C_MAX_CHAINS]; + + struct rtwn_rx_radiotap_header sc_rxtap; + struct rtwn_tx_radiotap_header sc_txtap; +}; + +/* + * MAC initialization values. + */ +static const struct { + uint16_t reg; + uint8_t val; +} rtl8192ce_mac[] = { + { 0x420, 0x80 }, { 0x423, 0x00 }, { 0x430, 0x00 }, { 0x431, 0x00 }, + { 0x432, 0x00 }, { 0x433, 0x01 }, { 0x434, 0x04 }, { 0x435, 0x05 }, + { 0x436, 0x06 }, { 0x437, 0x07 }, { 0x438, 0x00 }, { 0x439, 0x00 }, + { 0x43a, 0x00 }, { 0x43b, 0x01 }, { 0x43c, 0x04 }, { 0x43d, 0x05 }, + { 0x43e, 0x06 }, { 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 }, + { 0x442, 0x00 }, { 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f }, + { 0x447, 0x00 }, { 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 }, + { 0x45b, 0xb9 }, { 0x460, 0x88 }, { 0x461, 0x88 }, { 0x462, 0x06 }, + { 0x463, 0x03 }, { 0x4c8, 0x04 }, { 0x4c9, 0x08 }, { 0x4cc, 0x02 }, + { 0x4cd, 0x28 }, { 0x4ce, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 }, + { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 }, + { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 }, + { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 }, + { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a }, + { 0x515, 0x10 }, { 0x516, 0x0a }, { 0x517, 0x10 }, { 0x51a, 0x16 }, + { 0x524, 0x0f }, { 0x525, 0x4f }, { 0x546, 0x20 }, { 0x547, 0x00 }, + { 0x559, 0x02 }, { 0x55a, 0x02 }, { 0x55d, 0xff }, { 0x605, 0x30 }, + { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x652, 0x20 }, { 0x63c, 0x0a }, + { 0x63d, 0x0e }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 }, + { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 }, + { 0x70b, 0x87 } +}; + +/* + * Baseband initialization values. + */ +struct rtwn_bb_prog { + int count; + const uint16_t *regs; + const uint32_t *vals; + int agccount; + const uint32_t *agcvals; +}; + +/* + * RTL8192CU and RTL8192CE-VAU. + */ +static const uint16_t rtl8192ce_bb_regs[] = { + 0x024, 0x028, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, + 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, + 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, + 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, + 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, + 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, + 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08, + 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, + 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, + 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, + 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, + 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, + 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, + 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, + 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, + 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, + 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, + 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, + 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, + 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, + 0xed8, 0xedc, 0xee0, 0xeec, 0xf14, 0xf4c, 0xf00 +}; + +static const uint32_t rtl8192ce_bb_vals_2t[] = { + 0x0011800f, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, + 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, + 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, + 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, + 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, + 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, + 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, + 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, + 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, + 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, + 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, + 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, + 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, + 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, + 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, + 0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000, + 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, + 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, + 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, + 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, + 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, + 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, + 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, + 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, + 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, + 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, + 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, + 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, + 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, + 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, + 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, + 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, + 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, + 0x00000000, 0x00000300 +}; + +static const uint32_t rtl8192ce_bb_vals_1t[] = { + 0x0011800f, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, + 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, + 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, + 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, + 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, + 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, + 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, + 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, + 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, + 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, + 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, + 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, + 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, + 0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000, + 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, + 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, + 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, + 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, + 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, + 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, + 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, + 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, + 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, + 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, + 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, + 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, + 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, + 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x631b25a0, + 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, + 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, + 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, + 0x00000000, 0x00000300, +}; + +static const uint32_t rtl8192ce_agc_vals[] = { + 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, + 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, + 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, + 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, + 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, + 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, + 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, + 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, + 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, + 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, + 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, + 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, + 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, + 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, + 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, + 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, + 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, + 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, + 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, + 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, + 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, + 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, + 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, + 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, + 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, + 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, + 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, + 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, + 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, + 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, + 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, + 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e +}; + +static const struct rtwn_bb_prog rtl8192ce_bb_prog_2t = { + nitems(rtl8192ce_bb_regs), + rtl8192ce_bb_regs, + rtl8192ce_bb_vals_2t, + nitems(rtl8192ce_agc_vals), + rtl8192ce_agc_vals +}; + +static const struct rtwn_bb_prog rtl8192ce_bb_prog_1t = { + nitems(rtl8192ce_bb_regs), + rtl8192ce_bb_regs, + rtl8192ce_bb_vals_1t, + nitems(rtl8192ce_agc_vals), + rtl8192ce_agc_vals +}; + +/* + * RTL8188CU. + */ +static const uint32_t rtl8192cu_bb_vals[] = { + 0x0011800d, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, + 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, + 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, + 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, + 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, + 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, + 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, + 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, + 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, + 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, + 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, + 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, + 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, + 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, + 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, + 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, + 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x0186115b, + 0x0000001f, 0x00b99612, 0x40000100, 0x20f60000, 0x40000100, + 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, + 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, + 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, + 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, + 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, + 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, + 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, + 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, + 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, + 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, + 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, + 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, + 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, + 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, + 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, + 0x00000000, 0x00000300 +}; + +static const struct rtwn_bb_prog rtl8192cu_bb_prog = { + nitems(rtl8192ce_bb_regs), + rtl8192ce_bb_regs, + rtl8192cu_bb_vals, + nitems(rtl8192ce_agc_vals), + rtl8192ce_agc_vals +}; + +/* + * RTL8188CE-VAU. + */ +static const uint32_t rtl8188ce_bb_vals[] = { + 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, + 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, + 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, + 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, + 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, + 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, + 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, + 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, + 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, + 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, + 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, + 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, + 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, + 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, + 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, + 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, + 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, + 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, + 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, + 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, + 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, + 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, + 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, + 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, + 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, + 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, + 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, + 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, + 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, + 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, + 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, + 0x00000000, 0x00000300 +}; + +static const uint32_t rtl8188ce_agc_vals[] = { + 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, + 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, + 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, + 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, + 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, + 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, + 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, + 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, + 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, + 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, + 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, + 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, + 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, + 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, + 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, + 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, + 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, + 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, + 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, + 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, + 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, + 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, + 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, + 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, + 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, + 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, + 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, + 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, + 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, + 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, + 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, + 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e +}; + +static const struct rtwn_bb_prog rtl8188ce_bb_prog = { + nitems(rtl8192ce_bb_regs), + rtl8192ce_bb_regs, + rtl8188ce_bb_vals, + nitems(rtl8188ce_agc_vals), + rtl8188ce_agc_vals +}; + +static const uint32_t rtl8188cu_bb_vals[] = { + 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, + 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, + 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, + 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, + 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, + 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, + 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, + 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, + 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, + 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, + 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, + 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, + 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, + 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, + 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, + 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, + 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, + 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, + 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, + 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, + 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, + 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, + 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, + 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, + 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, + 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, + 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, + 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, + 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, + 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, + 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, + 0x00000000, 0x00000300 +}; + +static const struct rtwn_bb_prog rtl8188cu_bb_prog = { + nitems(rtl8192ce_bb_regs), + rtl8192ce_bb_regs, + rtl8188cu_bb_vals, + nitems(rtl8188ce_agc_vals), + rtl8188ce_agc_vals +}; + +/* + * RTL8188RU. + */ +static const uint16_t rtl8188ru_bb_regs[] = { + 0x024, 0x028, 0x040, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, + 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, + 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, + 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, + 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, + 0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, + 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, + 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, + 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, + 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, + 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, + 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, + 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, + 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, + 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, + 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, + 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, + 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, + 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, + 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, + 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xee8, 0xf14, 0xf4c, 0xf00 +}; + +static const uint32_t rtl8188ru_bb_vals[] = { + 0x0011800d, 0x00ffdb83, 0x000c0004, 0x80040000, 0x00000001, + 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, + 0x00000000, 0x01000100, 0x00390204, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, + 0x32323200, 0x03000300, 0x22004000, 0x00000808, 0x00ffc3f1, + 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, + 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, + 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, + 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, + 0x15160000, 0x070b0f12, 0x00000104, 0x00d30000, 0x101fbf00, + 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, + 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, + 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, + 0x007f037f, 0x6954342e, 0x43bc0094, 0x6954342f, 0x433c0094, + 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c56000d, + 0x018610db, 0x0000001f, 0x00b91612, 0x24000090, 0x20f60000, + 0x24000090, 0x20200000, 0x00121820, 0x00000000, 0x00121820, + 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, + 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, + 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, + 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, + 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, + 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, + 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, + 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, + 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, + 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, + 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, + 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, + 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, + 0x31555448, 0x00000003, 0x00000000, 0x00000300 +}; + +static const uint32_t rtl8188ru_agc_vals[] = { + 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, + 0x7b050001, 0x7b060001, 0x7b070001, 0x7b080001, 0x7a090001, + 0x790a0001, 0x780b0001, 0x770c0001, 0x760d0001, 0x750e0001, + 0x740f0001, 0x73100001, 0x72110001, 0x71120001, 0x70130001, + 0x6f140001, 0x6e150001, 0x6d160001, 0x6c170001, 0x6b180001, + 0x6a190001, 0x691a0001, 0x681b0001, 0x671c0001, 0x661d0001, + 0x651e0001, 0x641f0001, 0x63200001, 0x62210001, 0x61220001, + 0x60230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, + 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, + 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, + 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, + 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, + 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, + 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, + 0x7b460001, 0x7b470001, 0x7b480001, 0x7a490001, 0x794a0001, + 0x784b0001, 0x774c0001, 0x764d0001, 0x754e0001, 0x744f0001, + 0x73500001, 0x72510001, 0x71520001, 0x70530001, 0x6f540001, + 0x6e550001, 0x6d560001, 0x6c570001, 0x6b580001, 0x6a590001, + 0x695a0001, 0x685b0001, 0x675c0001, 0x665d0001, 0x655e0001, + 0x645f0001, 0x63600001, 0x62610001, 0x61620001, 0x60630001, + 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, + 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, + 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, + 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, + 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, + 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, + 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, + 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, + 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, + 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, + 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, + 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e +}; + +static const struct rtwn_bb_prog rtl8188ru_bb_prog = { + nitems(rtl8188ru_bb_regs), + rtl8188ru_bb_regs, + rtl8188ru_bb_vals, + nitems(rtl8188ru_agc_vals), + rtl8188ru_agc_vals +}; + +/* + * RF initialization values. + */ +struct rtwn_rf_prog { + int count; + const uint8_t *regs; + const uint32_t *vals; +}; + +/* + * RTL8192CU and RTL8192CE-VAU. + */ +static const uint8_t rtl8192ce_rf1_regs[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2a, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, + 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, + 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, + 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, + 0x2c, 0x2a, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, + 0x11, 0x10, 0x11, 0x10, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, + 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x00, + 0x18, 0xfe, 0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00 +}; + +static const uint32_t rtl8192ce_rf1_vals[] = { + 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, + 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, + 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, + 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, + 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, + 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, + 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, + 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, + 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, + 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, + 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, + 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, + 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, + 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, + 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, + 0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, + 0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, + 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, + 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, + 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, + 0x30159 +}; + +static const uint8_t rtl8192ce_rf2_regs[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16 +}; + +static const uint32_t rtl8192ce_rf2_vals[] = { + 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, + 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x32000, 0x71000, + 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493, + 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c, + 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, + 0xe0330, 0xa0330, 0x60330, 0x20330 +}; + +static const struct rtwn_rf_prog rtl8192ce_rf_prog[] = { + { + nitems(rtl8192ce_rf1_regs), + rtl8192ce_rf1_regs, + rtl8192ce_rf1_vals + }, + { + nitems(rtl8192ce_rf2_regs), + rtl8192ce_rf2_regs, + rtl8192ce_rf2_vals + } +}; + +/* + * RTL8188CE-VAU. + */ +static const uint32_t rtl8188ce_rf_vals[] = { + 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, + 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, + 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, + 0x00000, 0x01558, 0x00060, 0x00483, 0x4f200, 0xec7d9, 0x577c0, + 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, + 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, + 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, + 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, + 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, + 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, + 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, + 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, + 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, + 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, + 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, + 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, + 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, + 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, + 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, + 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, + 0x30159 +}; + +static const struct rtwn_rf_prog rtl8188ce_rf_prog[] = { + { + nitems(rtl8192ce_rf1_regs), + rtl8192ce_rf1_regs, + rtl8188ce_rf_vals + } +}; + + +/* + * RTL8188CU. + */ +static const uint32_t rtl8188cu_rf_vals[] = { + 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, + 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, + 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, + 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, + 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, + 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, + 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, + 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, + 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, + 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, + 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, + 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, + 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, + 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, + 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, + 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, + 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, + 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, + 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, + 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, + 0x30159 +}; + +static const struct rtwn_rf_prog rtl8188cu_rf_prog[] = { + { + nitems(rtl8192ce_rf1_regs), + rtl8192ce_rf1_regs, + rtl8188cu_rf_vals + } +}; + +/* + * RTL8188RU. + */ +static const uint32_t rtl8188ru_rf_vals[] = { + 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb0, + 0x54867, 0x8992e, 0x0e529, 0x39ce7, 0x00451, 0x00000, 0x00255, + 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, + 0x0083c, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x977c0, + 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, + 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, + 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, + 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, + 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, + 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, + 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, + 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, + 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, + 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, + 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0xd8000, + 0x90000, 0x51000, 0x12000, 0x28fb4, 0x24fa8, 0x207a4, 0x1c798, + 0x183a4, 0x14398, 0x101a4, 0x0c198, 0x080a4, 0x04098, 0x00014, + 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, + 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, + 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, + 0x30159 +}; + +static const struct rtwn_rf_prog rtl8188ru_rf_prog[] = { + { + nitems(rtl8192ce_rf1_regs), + rtl8192ce_rf1_regs, + rtl8188ru_rf_vals + } +}; + +struct rtwn_txpwr { + uint8_t pwr[3][28]; +}; + +/* + * Per RF chain/group/rate Tx gain values. + */ +static const struct rtwn_txpwr rtl8192cu_txagc[] = { + { { /* Chain 0. */ + { /* Group 0. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x0c, 0x0c, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* OFDM6~54. */ + 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* MCS0~7. */ + 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02 /* MCS8~15. */ + }, + { /* Group 1. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + }, + { /* Group 2. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + } + } }, + { { /* Chain 1. */ + { /* Group 0. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + }, + { /* Group 1. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + }, + { /* Group 2. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + } + } } +}; + +static const struct rtwn_txpwr rtl8188ru_txagc[] = { + { { /* Chain 0. */ + { /* Group 0. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x08, 0x08, 0x08, 0x06, 0x06, 0x04, 0x04, 0x00, /* OFDM6~54. */ + 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00, /* MCS0~7. */ + 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00 /* MCS8~15. */ + }, + { /* Group 1. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + }, + { /* Group 2. */ + 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ + } + } } +}; + +#define RTWN_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ + MTX_NETWORK_LOCK, MTX_DEF) +#define RTWN_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define RTWN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define RTWN_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define RTWN_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) + Property changes on: projects/clang380-import/sys/dev/rtwn/if_rtwnreg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/dev/usb/net/if_axe.c =================================================================== --- projects/clang380-import/sys/dev/usb/net/if_axe.c (revision 293015) +++ projects/clang380-import/sys/dev/usb/net/if_axe.c (revision 293016) @@ -1,1504 +1,1505 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000-2003 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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$"); /* * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. * Used in the LinkSys USB200M and various other adapters. * * Manuals available from: * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF * Note: you need the manual for the AX88170 chip (USB 1.x ethernet * controller) to find the definitions for the RX control register. * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF * * Written by Bill Paul * Senior Engineer * Wind River Systems */ /* * The AX88172 provides USB ethernet supports at 10 and 100Mbps. * It uses an external PHY (reference designs use a RealTek chip), * and has a 64-bit multicast hash filter. There is some information * missing from the manual which one needs to know in order to make * the chip function: * * - You must set bit 7 in the RX control register, otherwise the * chip won't receive any packets. * - You must initialize all 3 IPG registers, or you won't be able * to send any packets. * * Note that this device appears to only support loading the station * address via autload from the EEPROM (i.e. there's no way to manaully * set it). * * (Adam Weinberger wanted me to name this driver if_gir.c.) */ /* * Ax88178 and Ax88772 support backported from the OpenBSD driver. * 2007/02/12, J.R. Oldroyd, fbsd@opal.com * * Manual here: * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf */ #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 "usbdevs.h" #define USB_DEBUG_VAR axe_debug #include #include #include #include /* * AXE_178_MAX_FRAME_BURST * max frame burst size for Ax88178 and Ax88772 * 0 2048 bytes * 1 4096 bytes * 2 8192 bytes * 3 16384 bytes * use the largest your system can handle without USB stalling. * * NB: 88772 parts appear to generate lots of input errors with * a 2K rx buffer and 8K is only slightly faster than 4K on an * EHCI port on a T42 so change at your own risk. */ #define AXE_178_MAX_FRAME_BURST 1 #define AXE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) #ifdef USB_DEBUG static int axe_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe"); SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RWTUN, &axe_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const STRUCT_USB_HOST_ID axe_devs[] = { #define AXE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } AXE_DEV(ABOCOM, UF200, 0), AXE_DEV(ACERCM, EP1427X2, 0), AXE_DEV(APPLE, ETHERNET, AXE_FLAG_772), AXE_DEV(ASIX, AX88172, 0), AXE_DEV(ASIX, AX88178, AXE_FLAG_178), AXE_DEV(ASIX, AX88772, AXE_FLAG_772), AXE_DEV(ASIX, AX88772A, AXE_FLAG_772A), AXE_DEV(ASIX, AX88772B, AXE_FLAG_772B), AXE_DEV(ASIX, AX88772B_1, AXE_FLAG_772B), AXE_DEV(ATEN, UC210T, 0), AXE_DEV(BELKIN, F5D5055, AXE_FLAG_178), AXE_DEV(BILLIONTON, USB2AR, 0), AXE_DEV(CISCOLINKSYS, USB200MV2, AXE_FLAG_772A), AXE_DEV(COREGA, FETHER_USB2_TX, 0), AXE_DEV(DLINK, DUBE100, 0), AXE_DEV(DLINK, DUBE100B1, AXE_FLAG_772), AXE_DEV(DLINK, DUBE100C1, AXE_FLAG_772B), AXE_DEV(GOODWAY, GWUSB2E, 0), AXE_DEV(IODATA, ETGUS2, AXE_FLAG_178), AXE_DEV(JVC, MP_PRX1, 0), AXE_DEV(LENOVO, ETHERNET, AXE_FLAG_772B), AXE_DEV(LINKSYS2, USB200M, 0), AXE_DEV(LINKSYS4, USB1000, AXE_FLAG_178), AXE_DEV(LOGITEC, LAN_GTJU2A, AXE_FLAG_178), AXE_DEV(MELCO, LUAU2KTX, 0), AXE_DEV(MELCO, LUA3U2AGT, AXE_FLAG_178), AXE_DEV(NETGEAR, FA120, 0), AXE_DEV(OQO, ETHER01PLUS, AXE_FLAG_772), AXE_DEV(PLANEX3, GU1000T, AXE_FLAG_178), AXE_DEV(SITECOM, LN029, 0), AXE_DEV(SITECOMEU, LN028, AXE_FLAG_178), + AXE_DEV(SITECOMEU, LN031, AXE_FLAG_178), AXE_DEV(SYSTEMTALKS, SGCX2UL, 0), #undef AXE_DEV }; static device_probe_t axe_probe; static device_attach_t axe_attach; static device_detach_t axe_detach; static usb_callback_t axe_bulk_read_callback; static usb_callback_t axe_bulk_write_callback; static miibus_readreg_t axe_miibus_readreg; static miibus_writereg_t axe_miibus_writereg; static miibus_statchg_t axe_miibus_statchg; static uether_fn_t axe_attach_post; static uether_fn_t axe_init; static uether_fn_t axe_stop; static uether_fn_t axe_start; static uether_fn_t axe_tick; static uether_fn_t axe_setmulti; static uether_fn_t axe_setpromisc; static int axe_attach_post_sub(struct usb_ether *); static int axe_ifmedia_upd(struct ifnet *); static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int axe_cmd(struct axe_softc *, int, int, int, void *); static void axe_ax88178_init(struct axe_softc *); static void axe_ax88772_init(struct axe_softc *); static void axe_ax88772_phywake(struct axe_softc *); static void axe_ax88772a_init(struct axe_softc *); static void axe_ax88772b_init(struct axe_softc *); static int axe_get_phyno(struct axe_softc *, int); static int axe_ioctl(struct ifnet *, u_long, caddr_t); static int axe_rx_frame(struct usb_ether *, struct usb_page_cache *, int); static int axe_rxeof(struct usb_ether *, struct usb_page_cache *, unsigned int offset, unsigned int, struct axe_csum_hdr *); static void axe_csum_cfg(struct usb_ether *); static const struct usb_config axe_config[AXE_N_TRANSFER] = { [AXE_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .frames = 16, .bufsize = 16 * MCLBYTES, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = axe_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [AXE_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 16384, /* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = axe_bulk_read_callback, .timeout = 0, /* no timeout */ }, }; static const struct ax88772b_mfb ax88772b_mfb_table[] = { { 0x8000, 0x8001, 2048 }, { 0x8100, 0x8147, 4096}, { 0x8200, 0x81EB, 6144}, { 0x8300, 0x83D7, 8192}, { 0x8400, 0x851E, 16384}, { 0x8500, 0x8666, 20480}, { 0x8600, 0x87AE, 24576}, { 0x8700, 0x8A3D, 32768} }; static device_method_t axe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, axe_probe), DEVMETHOD(device_attach, axe_attach), DEVMETHOD(device_detach, axe_detach), /* MII interface */ DEVMETHOD(miibus_readreg, axe_miibus_readreg), DEVMETHOD(miibus_writereg, axe_miibus_writereg), DEVMETHOD(miibus_statchg, axe_miibus_statchg), DEVMETHOD_END }; static driver_t axe_driver = { .name = "axe", .methods = axe_methods, .size = sizeof(struct axe_softc), }; static devclass_t axe_devclass; DRIVER_MODULE(axe, uhub, axe_driver, axe_devclass, NULL, 0); DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(axe, uether, 1, 1, 1); MODULE_DEPEND(axe, usb, 1, 1, 1); MODULE_DEPEND(axe, ether, 1, 1, 1); MODULE_DEPEND(axe, miibus, 1, 1, 1); MODULE_VERSION(axe, 1); USB_PNP_HOST_INFO(axe_devs); static const struct usb_ether_methods axe_ue_methods = { .ue_attach_post = axe_attach_post, .ue_attach_post_sub = axe_attach_post_sub, .ue_start = axe_start, .ue_init = axe_init, .ue_stop = axe_stop, .ue_tick = axe_tick, .ue_setmulti = axe_setmulti, .ue_setpromisc = axe_setpromisc, .ue_mii_upd = axe_ifmedia_upd, .ue_mii_sts = axe_ifmedia_sts, }; static int axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) { struct usb_device_request req; usb_error_t err; AXE_LOCK_ASSERT(sc, MA_OWNED); req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ? UT_WRITE_VENDOR_DEVICE : UT_READ_VENDOR_DEVICE); req.bRequest = AXE_CMD_CMD(cmd); USETW(req.wValue, val); USETW(req.wIndex, index); USETW(req.wLength, AXE_CMD_LEN(cmd)); err = uether_do_request(&sc->sc_ue, &req, buf, 1000); return (err); } static int axe_miibus_readreg(device_t dev, int phy, int reg) { struct axe_softc *sc = device_get_softc(dev); uint16_t val; int locked; locked = mtx_owned(&sc->sc_mtx); if (!locked) AXE_LOCK(sc); axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); val = le16toh(val); if (AXE_IS_772(sc) && reg == MII_BMSR) { /* * BMSR of AX88772 indicates that it supports extended * capability but the extended status register is * revered for embedded ethernet PHY. So clear the * extended capability bit of BMSR. */ val &= ~BMSR_EXTCAP; } if (!locked) AXE_UNLOCK(sc); return (val); } static int axe_miibus_writereg(device_t dev, int phy, int reg, int val) { struct axe_softc *sc = device_get_softc(dev); int locked; val = htole32(val); locked = mtx_owned(&sc->sc_mtx); if (!locked) AXE_LOCK(sc); axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); if (!locked) AXE_UNLOCK(sc); return (0); } static void axe_miibus_statchg(device_t dev) { struct axe_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); struct ifnet *ifp; uint16_t val; int err, locked; locked = mtx_owned(&sc->sc_mtx); if (!locked) AXE_LOCK(sc); ifp = uether_getifp(&sc->sc_ue); if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; sc->sc_flags &= ~AXE_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->sc_flags |= AXE_FLAG_LINK; break; case IFM_1000_T: if ((sc->sc_flags & AXE_FLAG_178) == 0) break; sc->sc_flags |= AXE_FLAG_LINK; break; default: break; } } /* Lost link, do nothing. */ if ((sc->sc_flags & AXE_FLAG_LINK) == 0) goto done; val = 0; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { val |= AXE_MEDIA_FULL_DUPLEX; if (AXE_IS_178_FAMILY(sc)) { if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN; } } if (AXE_IS_178_FAMILY(sc)) { val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; if ((sc->sc_flags & AXE_FLAG_178) != 0) val |= AXE_178_MEDIA_ENCK; switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; break; case IFM_100_TX: val |= AXE_178_MEDIA_100TX; break; case IFM_10_T: /* doesn't need to be handled */ break; } } err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); if (err) device_printf(dev, "media change failed, error %d\n", err); done: if (!locked) AXE_UNLOCK(sc); } /* * Set media options. */ static int axe_ifmedia_upd(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); struct mii_softc *miisc; int error; AXE_LOCK_ASSERT(sc, MA_OWNED); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); error = mii_mediachg(mii); return (error); } /* * Report current media status. */ static void axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct axe_softc *sc = ifp->if_softc; struct mii_data *mii = GET_MII(sc); AXE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; AXE_UNLOCK(sc); } static void axe_setmulti(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); struct ifmultiaddr *ifma; uint32_t h = 0; uint16_t rxmode; uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; AXE_LOCK_ASSERT(sc, MA_OWNED); axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { rxmode |= AXE_RXCMD_ALLMULTI; axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); return; } rxmode &= ~AXE_RXCMD_ALLMULTI; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; hashtbl[h / 8] |= 1 << (h % 8); } if_maddr_runlock(ifp); axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); } static int axe_get_phyno(struct axe_softc *sc, int sel) { int phyno; switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) { case PHY_TYPE_100_HOME: case PHY_TYPE_GIG: phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]); break; case PHY_TYPE_SPECIAL: /* FALLTHROUGH */ case PHY_TYPE_RSVD: /* FALLTHROUGH */ case PHY_TYPE_NON_SUP: /* FALLTHROUGH */ default: phyno = -1; break; } return (phyno); } #define AXE_GPIO_WRITE(x, y) do { \ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ uether_pause(ue, (y)); \ } while (0) static void axe_ax88178_init(struct axe_softc *sc) { struct usb_ether *ue; int gpio0, ledmode, phymode; uint16_t eeprom, val; ue = &sc->sc_ue; axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); /* XXX magic */ axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); eeprom = le16toh(eeprom); axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); /* if EEPROM is invalid we have to use to GPIO0 */ if (eeprom == 0xffff) { phymode = AXE_PHY_MODE_MARVELL; gpio0 = 1; ledmode = 0; } else { phymode = eeprom & 0x7f; gpio0 = (eeprom & 0x80) ? 0 : 1; ledmode = eeprom >> 8; } if (bootverbose) device_printf(sc->sc_ue.ue_dev, "EEPROM data : 0x%04x, phymode : 0x%02x\n", eeprom, phymode); /* Program GPIOs depending on PHY hardware. */ switch (phymode) { case AXE_PHY_MODE_MARVELL: if (gpio0 == 1) { AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, hz / 32); AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); } else { AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | AXE_GPIO1_EN, hz / 3); if (ledmode == 1) { AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, hz / 3); } else { AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); } } break; case AXE_PHY_MODE_CICADA: case AXE_PHY_MODE_CICADA_V2: case AXE_PHY_MODE_CICADA_V2_ASIX: if (gpio0 == 1) AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | AXE_GPIO0_EN, hz / 32); else AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | AXE_GPIO1_EN, hz / 32); break; case AXE_PHY_MODE_AGERE: AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | AXE_GPIO1_EN, hz / 32); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); break; case AXE_PHY_MODE_REALTEK_8211CL: case AXE_PHY_MODE_REALTEK_8211BN: case AXE_PHY_MODE_REALTEK_8251CL: val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : AXE_GPIO1 | AXE_GPIO1_EN; AXE_GPIO_WRITE(val, hz / 32); AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, 0x1F, 0x0005); axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, 0x0C, 0x0000); val = axe_miibus_readreg(ue->ue_dev, sc->sc_phyno, 0x0001); axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, 0x01, val | 0x0080); axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, 0x1F, 0x0000); } break; default: /* Unknown PHY model or no need to program GPIOs. */ break; } /* soft reset */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); uether_pause(ue, hz / 4); axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); uether_pause(ue, hz / 4); /* Enable MII/GMII/RGMII interface to work with external PHY. */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); uether_pause(ue, hz / 4); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } static void axe_ax88772_init(struct axe_softc *sc) { axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); uether_pause(&sc->sc_ue, hz / 16); if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { /* ask for the embedded PHY */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); uether_pause(&sc->sc_ue, hz / 64); /* power down and reset state, pin reset state */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); uether_pause(&sc->sc_ue, hz / 16); /* power down/reset state, pin operating state */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); uether_pause(&sc->sc_ue, hz / 4); /* power up, reset */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); /* power up, operating */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); } else { /* ask for external PHY */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); uether_pause(&sc->sc_ue, hz / 64); /* power down internal PHY */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); } uether_pause(&sc->sc_ue, hz / 4); axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } static void axe_ax88772_phywake(struct axe_softc *sc) { struct usb_ether *ue; ue = &sc->sc_ue; if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { /* Manually select internal(embedded) PHY - MAC mode. */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | AXE_SW_PHY_SELECT_EMBEDDED | AXE_SW_PHY_SELECT_SS_MII, NULL); uether_pause(&sc->sc_ue, hz / 32); } else { /* * Manually select external PHY - MAC mode. * Reverse MII/RMII is for AX88772A PHY mode. */ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL); uether_pause(&sc->sc_ue, hz / 32); } /* Take PHY out of power down. */ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_IPRL, NULL); uether_pause(&sc->sc_ue, hz / 4); axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); uether_pause(&sc->sc_ue, hz); axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); uether_pause(&sc->sc_ue, hz / 32); axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); uether_pause(&sc->sc_ue, hz / 32); } static void axe_ax88772a_init(struct axe_softc *sc) { struct usb_ether *ue; ue = &sc->sc_ue; /* Reload EEPROM. */ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); axe_ax88772_phywake(sc); /* Stop MAC. */ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } static void axe_ax88772b_init(struct axe_softc *sc) { struct usb_ether *ue; uint16_t eeprom; uint8_t *eaddr; int i; ue = &sc->sc_ue; /* Reload EEPROM. */ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); /* * Save PHY power saving configuration(high byte) and * clear EEPROM checksum value(low byte). */ axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, &eeprom); sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00; /* * Auto-loaded default station address from internal ROM is * 00:00:00:00:00:00 such that an explicit access to EEPROM * is required to get real station address. */ eaddr = ue->ue_eaddr; for (i = 0; i < ETHER_ADDR_LEN / 2; i++) { axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_NODE_ID + i, &eeprom); eeprom = le16toh(eeprom); *eaddr++ = (uint8_t)(eeprom & 0xFF); *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF); } /* Wakeup PHY. */ axe_ax88772_phywake(sc); /* Stop MAC. */ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } #undef AXE_GPIO_WRITE static void axe_reset(struct axe_softc *sc) { struct usb_config_descriptor *cd; usb_error_t err; cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) DPRINTF("reset failed (ignored)\n"); /* Wait a little while for the chip to get its brains in order. */ uether_pause(&sc->sc_ue, hz / 100); /* Reinitialize controller to achieve full reset. */ if (sc->sc_flags & AXE_FLAG_178) axe_ax88178_init(sc); else if (sc->sc_flags & AXE_FLAG_772) axe_ax88772_init(sc); else if (sc->sc_flags & AXE_FLAG_772A) axe_ax88772a_init(sc); else if (sc->sc_flags & AXE_FLAG_772B) axe_ax88772b_init(sc); } static void axe_attach_post(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); /* * Load PHY indexes first. Needed by axe_xxx_init(). */ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); if (bootverbose) device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n", sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); if (sc->sc_phyno == -1) sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); if (sc->sc_phyno == -1) { device_printf(sc->sc_ue.ue_dev, "no valid PHY address found, assuming PHY address 0\n"); sc->sc_phyno = 0; } /* Initialize controller and get station address. */ if (sc->sc_flags & AXE_FLAG_178) { axe_ax88178_init(sc); axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); } else if (sc->sc_flags & AXE_FLAG_772) { axe_ax88772_init(sc); axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); } else if (sc->sc_flags & AXE_FLAG_772A) { axe_ax88772a_init(sc); axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); } else if (sc->sc_flags & AXE_FLAG_772B) { axe_ax88772b_init(sc); } else axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); /* * Fetch IPG values. */ if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B)) { /* Set IPG values. */ sc->sc_ipgs[0] = 0x15; sc->sc_ipgs[1] = 0x16; sc->sc_ipgs[2] = 0x1A; } else axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); } static int axe_attach_post_sub(struct usb_ether *ue) { struct axe_softc *sc; struct ifnet *ifp; u_int adv_pause; int error; sc = uether_getsc(ue); ifp = ue->ue_ifp; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = uether_start; ifp->if_ioctl = axe_ioctl; ifp->if_init = uether_init; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); if (AXE_IS_178_FAMILY(sc)) ifp->if_capabilities |= IFCAP_VLAN_MTU; if (sc->sc_flags & AXE_FLAG_772B) { ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_RXCSUM; ifp->if_hwassist = AXE_CSUM_FEATURES; /* * Checksum offloading of AX88772B also works with VLAN * tagged frames but there is no way to take advantage * of the feature because vlan(4) assumes * IFCAP_VLAN_HWTAGGING is prerequisite condition to * support checksum offloading with VLAN. VLAN hardware * tagging support of AX88772B is very limited so it's * not possible to announce IFCAP_VLAN_HWTAGGING. */ } ifp->if_capenable = ifp->if_capabilities; if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B | AXE_FLAG_178)) adv_pause = MIIF_DOPAUSE; else adv_pause = 0; mtx_lock(&Giant); error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, adv_pause); mtx_unlock(&Giant); return (error); } /* * Probe for a AX88172 chip. */ static int axe_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != AXE_CONFIG_IDX) return (ENXIO); if (uaa->info.bIfaceIndex != AXE_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int axe_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct axe_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int error; sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); iface_index = AXE_IFACE_IDX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed\n"); goto detach; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &axe_ue_methods; error = uether_ifattach(ue); if (error) { device_printf(dev, "could not attach interface\n"); goto detach; } return (0); /* success */ detach: axe_detach(dev); return (ENXIO); /* failure */ } static int axe_detach(device_t dev) { struct axe_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } #if (AXE_BULK_BUF_SIZE >= 0x10000) #error "Please update axe_bulk_read_callback()!" #endif static void axe_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct axe_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); axe_rx_frame(ue, pc, actlen); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: /* Error */ DPRINTF("bulk read error, %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static int axe_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen) { struct axe_softc *sc; struct axe_sframe_hdr hdr; struct axe_csum_hdr csum_hdr; int error, len, pos; sc = uether_getsc(ue); pos = 0; len = 0; error = 0; if ((sc->sc_flags & AXE_FLAG_STD_FRAME) != 0) { while (pos < actlen) { if ((int)(pos + sizeof(hdr)) > actlen) { /* too little data */ error = EINVAL; break; } usbd_copy_out(pc, pos, &hdr, sizeof(hdr)); if ((hdr.len ^ hdr.ilen) != sc->sc_lenmask) { /* we lost sync */ error = EINVAL; break; } pos += sizeof(hdr); len = le16toh(hdr.len); if (pos + len > actlen) { /* invalid length */ error = EINVAL; break; } axe_rxeof(ue, pc, pos, len, NULL); pos += len + (len % 2); } } else if ((sc->sc_flags & AXE_FLAG_CSUM_FRAME) != 0) { while (pos < actlen) { if ((int)(pos + sizeof(csum_hdr)) > actlen) { /* too little data */ error = EINVAL; break; } usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr)); csum_hdr.len = le16toh(csum_hdr.len); csum_hdr.ilen = le16toh(csum_hdr.ilen); csum_hdr.cstatus = le16toh(csum_hdr.cstatus); if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^ AXE_CSUM_RXBYTES(csum_hdr.ilen)) != sc->sc_lenmask) { /* we lost sync */ error = EINVAL; break; } /* * Get total transferred frame length including * checksum header. The length should be multiple * of 4. */ len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len); len = (len + 3) & ~3; if (pos + len > actlen) { /* invalid length */ error = EINVAL; break; } axe_rxeof(ue, pc, pos + sizeof(csum_hdr), AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr); pos += len; } } else axe_rxeof(ue, pc, 0, actlen, NULL); if (error != 0) if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1); return (error); } static int axe_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset, unsigned int len, struct axe_csum_hdr *csum_hdr) { struct ifnet *ifp = ue->ue_ifp; struct mbuf *m; if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) { if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (EINVAL); } m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); return (ENOMEM); } m->m_len = m->m_pkthdr.len = MCLBYTES; m_adj(m, ETHER_ALIGN); usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = len; if (csum_hdr != NULL && csum_hdr->cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) { if ((csum_hdr->cstatus & (AXE_CSUM_HDR_L4_CSUM_ERR | AXE_CSUM_HDR_L3_CSUM_ERR)) == 0) { m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; if ((csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) == AXE_CSUM_HDR_L4_TYPE_TCP || (csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) == AXE_CSUM_HDR_L4_TYPE_UDP) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } } _IF_ENQUEUE(&ue->ue_rxq, m); return (0); } #if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4))) #error "Please update axe_bulk_write_callback()!" #endif static void axe_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct axe_softc *sc = usbd_xfer_softc(xfer); struct axe_sframe_hdr hdr; struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; int nframes, pos; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if ((sc->sc_flags & AXE_FLAG_LINK) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { /* * Don't send anything if there is no link or * controller is busy. */ return; } for (nframes = 0; nframes < 16 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, nframes); pos = 0; pc = usbd_xfer_get_frame(xfer, nframes); if (AXE_IS_178_FAMILY(sc)) { hdr.len = htole16(m->m_pkthdr.len); hdr.ilen = ~hdr.len; /* * If upper stack computed checksum, driver * should tell controller not to insert * computed checksum for checksum offloading * enabled controller. */ if (ifp->if_capabilities & IFCAP_TXCSUM) { if ((m->m_pkthdr.csum_flags & AXE_CSUM_FEATURES) != 0) hdr.len |= htole16( AXE_TX_CSUM_PSEUDO_HDR); else hdr.len |= htole16( AXE_TX_CSUM_DIS); } usbd_copy_in(pc, pos, &hdr, sizeof(hdr)); pos += sizeof(hdr); usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); pos += m->m_pkthdr.len; if ((pos % 512) == 0) { hdr.len = 0; hdr.ilen = 0xffff; usbd_copy_in(pc, pos, &hdr, sizeof(hdr)); pos += sizeof(hdr); } } else { usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); pos += m->m_pkthdr.len; } /* * XXX * Update TX packet counter here. This is not * correct way but it seems that there is no way * to know how many packets are sent at the end * of transfer because controller combines * multiple writes into single one if there is * room in TX buffer of controller. */ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); /* Set frame length. */ usbd_xfer_set_frame_len(xfer, nframes, pos); } if (nframes != 0) { usbd_xfer_set_frames(xfer, nframes); usbd_transfer_submit(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; } return; /* NOTREACHED */ default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void axe_tick(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); struct mii_data *mii = GET_MII(sc); AXE_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { axe_miibus_statchg(ue->ue_dev); if ((sc->sc_flags & AXE_FLAG_LINK) != 0) axe_start(ue); } } static void axe_start(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); /* * start the USB transfers, if not already started: */ usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]); } static void axe_csum_cfg(struct usb_ether *ue) { struct axe_softc *sc; struct ifnet *ifp; uint16_t csum1, csum2; sc = uether_getsc(ue); AXE_LOCK_ASSERT(sc, MA_OWNED); if ((sc->sc_flags & AXE_FLAG_772B) != 0) { ifp = uether_getifp(ue); csum1 = 0; csum2 = 0; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) csum1 |= AXE_TXCSUM_IP | AXE_TXCSUM_TCP | AXE_TXCSUM_UDP; axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL); csum1 = 0; csum2 = 0; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) csum1 |= AXE_RXCSUM_IP | AXE_RXCSUM_IPVE | AXE_RXCSUM_TCP | AXE_RXCSUM_UDP | AXE_RXCSUM_ICMP | AXE_RXCSUM_IGMP; axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL); } } static void axe_init(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); uint16_t rxmode; AXE_LOCK_ASSERT(sc, MA_OWNED); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* Cancel pending I/O */ axe_stop(ue); axe_reset(sc); /* Set MAC address and transmitter IPG values. */ if (AXE_IS_178_FAMILY(sc)) { axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp)); axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); } else { axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp)); axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL); } if (AXE_IS_178_FAMILY(sc)) { sc->sc_flags &= ~(AXE_FLAG_STD_FRAME | AXE_FLAG_CSUM_FRAME); if ((sc->sc_flags & AXE_FLAG_772B) != 0 && (ifp->if_capenable & IFCAP_RXCSUM) != 0) { sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK; sc->sc_flags |= AXE_FLAG_CSUM_FRAME; } else { sc->sc_lenmask = AXE_HDR_LEN_MASK; sc->sc_flags |= AXE_FLAG_STD_FRAME; } } /* Configure TX/RX checksum offloading. */ axe_csum_cfg(ue); if (sc->sc_flags & AXE_FLAG_772B) { /* AX88772B uses different maximum frame burst configuration. */ axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG, ax88772b_mfb_table[AX88772B_MFB_16K].threshold, ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL); } /* Enable receiver, set RX mode. */ rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); if (AXE_IS_178_FAMILY(sc)) { if (sc->sc_flags & AXE_FLAG_772B) { /* * Select RX header format type 1. Aligning IP * header on 4 byte boundary is not needed when * checksum offloading feature is not used * because we always copy the received frame in * RX handler. When RX checksum offloading is * active, aligning IP header is required to * reflect actual frame length including RX * header size. */ rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN; } else { /* * Default Rx buffer size is too small to get * maximum performance. */ rxmode |= AXE_178_RXCMD_MFB_16384; } } else { rxmode |= AXE_172_RXCMD_UNICAST; } /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) rxmode |= AXE_RXCMD_PROMISC; if (ifp->if_flags & IFF_BROADCAST) rxmode |= AXE_RXCMD_BROADCAST; axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); /* Load the multicast filter. */ axe_setmulti(ue); usbd_xfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]); ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Switch to selected media. */ axe_ifmedia_upd(ifp); } static void axe_setpromisc(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); uint16_t rxmode; axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (ifp->if_flags & IFF_PROMISC) { rxmode |= AXE_RXCMD_PROMISC; } else { rxmode &= ~AXE_RXCMD_PROMISC; } axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); axe_setmulti(ue); } static void axe_stop(struct usb_ether *ue) { struct axe_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); AXE_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->sc_flags &= ~AXE_FLAG_LINK; /* * stop all the transfers, if not already stopped: */ usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]); } static int axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct usb_ether *ue = ifp->if_softc; struct axe_softc *sc; struct ifreq *ifr; int error, mask, reinit; sc = uether_getsc(ue); ifr = (struct ifreq *)data; error = 0; reinit = 0; if (cmd == SIOCSIFCAP) { AXE_LOCK(sc); mask = ifr->ifr_reqcap ^ ifp->if_capenable; if ((mask & IFCAP_TXCSUM) != 0 && (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { ifp->if_capenable ^= IFCAP_TXCSUM; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist |= AXE_CSUM_FEATURES; else ifp->if_hwassist &= ~AXE_CSUM_FEATURES; reinit++; } if ((mask & IFCAP_RXCSUM) != 0 && (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { ifp->if_capenable ^= IFCAP_RXCSUM; reinit++; } if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) ifp->if_drv_flags &= ~IFF_DRV_RUNNING; else reinit = 0; AXE_UNLOCK(sc); if (reinit > 0) uether_init(ue); } else error = uether_ioctl(ifp, cmd, data); return (error); } Index: projects/clang380-import/sys/dev/usb/usbdevs =================================================================== --- projects/clang380-import/sys/dev/usb/usbdevs (revision 293015) +++ projects/clang380-import/sys/dev/usb/usbdevs (revision 293016) @@ -1,4699 +1,4700 @@ $FreeBSD$ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ /*- * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 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. */ /* * List of known USB vendors * * USB.org publishes a VID list of USB-IF member companies at * http://www.usb.org/developers/tools * Note that it does not show companies that have obtained a Vendor ID * without becoming full members. * * Please note that these IDs do not do anything. Adding an ID here and * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name * available to the source code and does not change any functionality, nor * does it make your device available to a specific driver. * It will however make the descriptive string available if a device does not * provide the string itself. * * After adding a vendor ID VNDR and a product ID PRDCT you will have the * following extra defines: * #define USB_VENDOR_VNDR 0x???? * #define USB_PRODUCT_VNDR_PRDCT 0x???? * * You may have to add these defines to the respective probe routines to * make the device recognised by the appropriate device driver. */ vendor UNKNOWN1 0x0053 Unknown vendor vendor UNKNOWN2 0x0105 Unknown vendor vendor EGALAX2 0x0123 eGalax, Inc. vendor CHIPSBANK 0x0204 Chipsbank Microelectronics Co. vendor HUMAX 0x02ad HUMAX vendor LTS 0x0386 LTS vendor BWCT 0x03da Bernd Walter Computer Technology vendor AOX 0x03e8 AOX vendor THESYS 0x03e9 Thesys vendor DATABROADCAST 0x03ea Data Broadcasting vendor ATMEL 0x03eb Atmel vendor IWATSU 0x03ec Iwatsu America vendor MITSUMI 0x03ee Mitsumi vendor HP 0x03f0 Hewlett Packard vendor GENOA 0x03f1 Genoa vendor OAK 0x03f2 Oak vendor ADAPTEC 0x03f3 Adaptec vendor DIEBOLD 0x03f4 Diebold vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical vendor EPSONIMAGING 0x03f8 Epson Imaging vendor KEYTRONIC 0x03f9 KeyTronic vendor OPTI 0x03fb OPTi vendor ELITEGROUP 0x03fc Elitegroup vendor XILINX 0x03fd Xilinx vendor FARALLON 0x03fe Farallon Communications vendor NATIONAL 0x0400 National Semiconductor vendor NATIONALREG 0x0401 National Registry vendor ACERLABS 0x0402 Acer Labs vendor FTDI 0x0403 Future Technology Devices vendor NCR 0x0404 NCR vendor SYNOPSYS2 0x0405 Synopsys vendor FUJITSUICL 0x0406 Fujitsu-ICL vendor FUJITSU2 0x0407 Fujitsu Personal Systems vendor QUANTA 0x0408 Quanta vendor NEC 0x0409 NEC vendor KODAK 0x040a Eastman Kodak vendor WELTREND 0x040b Weltrend vendor VIA 0x040d VIA vendor MCCI 0x040e MCCI vendor MELCO 0x0411 Melco vendor LEADTEK 0x0413 Leadtek vendor WINBOND 0x0416 Winbond vendor PHOENIX 0x041a Phoenix vendor CREATIVE 0x041e Creative Labs vendor NOKIA 0x0421 Nokia vendor ADI 0x0422 ADI Systems vendor CATC 0x0423 Computer Access Technology vendor SMC2 0x0424 Standard Microsystems vendor MOTOROLA_HK 0x0425 Motorola HK vendor GRAVIS 0x0428 Advanced Gravis Computer vendor CIRRUSLOGIC 0x0429 Cirrus Logic vendor INNOVATIVE 0x042c Innovative Semiconductors vendor MOLEX 0x042f Molex vendor SUN 0x0430 Sun Microsystems vendor UNISYS 0x0432 Unisys vendor TAUGA 0x0436 Taugagreining HF vendor AMD 0x0438 Advanced Micro Devices vendor LEXMARK 0x043d Lexmark International vendor LG 0x043e LG Electronics vendor NANAO 0x0440 NANAO vendor GATEWAY 0x0443 Gateway 2000 vendor NMB 0x0446 NMB vendor ALPS 0x044e Alps Electric vendor THRUST 0x044f Thrustmaster vendor TI 0x0451 Texas Instruments vendor ANALOGDEVICES 0x0456 Analog Devices vendor SIS 0x0457 Silicon Integrated Systems Corp. vendor KYE 0x0458 KYE Systems vendor DIAMOND2 0x045a Diamond (Supra) vendor RENESAS 0x045b Renesas vendor MICROSOFT 0x045e Microsoft vendor PRIMAX 0x0461 Primax Electronics vendor MGE 0x0463 MGE UPS Systems vendor AMP 0x0464 AMP vendor CHERRY 0x046a Cherry Mikroschalter vendor MEGATRENDS 0x046b American Megatrends vendor LOGITECH 0x046d Logitech vendor BTC 0x046e Behavior Tech. Computer vendor PHILIPS 0x0471 Philips vendor SUN2 0x0472 Sun Microsystems (offical) vendor SANYO 0x0474 Sanyo Electric vendor SEAGATE 0x0477 Seagate vendor CONNECTIX 0x0478 Connectix vendor SEMTECH 0x047a Semtech vendor KENSINGTON 0x047d Kensington vendor LUCENT 0x047e Lucent vendor PLANTRONICS 0x047f Plantronics vendor KYOCERA 0x0482 Kyocera Wireless Corp. vendor STMICRO 0x0483 STMicroelectronics vendor FOXCONN 0x0489 Foxconn vendor MEIZU 0x0492 Meizu Electronics vendor YAMAHA 0x0499 YAMAHA vendor COMPAQ 0x049f Compaq vendor HITACHI 0x04a4 Hitachi vendor ACERP 0x04a5 Acer Peripherals vendor DAVICOM 0x04a6 Davicom vendor VISIONEER 0x04a7 Visioneer vendor CANON 0x04a9 Canon vendor NIKON 0x04b0 Nikon vendor PAN 0x04b1 Pan International vendor IBM 0x04b3 IBM vendor CYPRESS 0x04b4 Cypress Semiconductor vendor ROHM 0x04b5 ROHM vendor COMPAL 0x04b7 Compal vendor EPSON 0x04b8 Seiko Epson vendor RAINBOW 0x04b9 Rainbow Technologies vendor IODATA 0x04bb I-O Data vendor TDK 0x04bf TDK vendor 3COMUSR 0x04c1 U.S. Robotics vendor METHODE 0x04c2 Methode Electronics Far East vendor MAXISWITCH 0x04c3 Maxi Switch vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research vendor FUJITSU 0x04c5 Fujitsu vendor TOSHIBAAM 0x04c6 Toshiba America vendor MICROMACRO 0x04c7 Micro Macro Technologies vendor KONICA 0x04c8 Konica vendor LITEON 0x04ca Lite-On Technology vendor FUJIPHOTO 0x04cb Fuji Photo Film vendor PHILIPSSEMI 0x04cc Philips Semiconductors vendor TATUNG 0x04cd Tatung Co. Of America vendor SCANLOGIC 0x04ce ScanLogic vendor MYSON 0x04cf Myson Technology vendor DIGI2 0x04d0 Digi vendor ITTCANON 0x04d1 ITT Canon vendor ALTEC 0x04d2 Altec Lansing vendor LSI 0x04d4 LSI vendor MENTORGRAPHICS 0x04d6 Mentor Graphics vendor ITUNERNET 0x04d8 I-Tuner Networks vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc. vendor PANASONIC 0x04da Panasonic (Matsushita) vendor HUANHSIN 0x04dc Huan Hsin vendor SHARP 0x04dd Sharp vendor IIYAMA 0x04e1 Iiyama vendor SHUTTLE 0x04e6 Shuttle Technology vendor ELO 0x04e7 Elo TouchSystems vendor SAMSUNG 0x04e8 Samsung Electronics vendor NORTHSTAR 0x04eb Northstar vendor TOKYOELECTRON 0x04ec Tokyo Electron vendor ANNABOOKS 0x04ed Annabooks vendor JVC 0x04f1 JVC vendor CHICONY 0x04f2 Chicony Electronics vendor ELAN 0x04f3 Elan vendor NEWNEX 0x04f7 Newnex vendor BROTHER 0x04f9 Brother Industries vendor DALLAS 0x04fa Dallas Semiconductor vendor AIPTEK2 0x04fc AIPTEK International vendor PFU 0x04fe PFU vendor FUJIKURA 0x0501 Fujikura/DDK vendor ACER 0x0502 Acer vendor 3COM 0x0506 3Com vendor HOSIDEN 0x0507 Hosiden Corporation vendor AZTECH 0x0509 Aztech Systems vendor BELKIN 0x050d Belkin Components vendor KAWATSU 0x050f Kawatsu Semiconductor vendor FCI 0x0514 FCI vendor LONGWELL 0x0516 Longwell vendor COMPOSITE 0x0518 Composite vendor STAR 0x0519 Star Micronics vendor APC 0x051d American Power Conversion vendor SCIATLANTA 0x051e Scientific Atlanta vendor TSM 0x0520 TSM vendor CONNECTEK 0x0522 Advanced Connectek USA vendor NETCHIP 0x0525 NetChip Technology vendor ALTRA 0x0527 ALTRA vendor ATI 0x0528 ATI Technologies vendor AKS 0x0529 Aladdin Knowledge Systems vendor TEKOM 0x052b Tekom vendor CANONDEV 0x052c Canon vendor WACOMTECH 0x0531 Wacom vendor INVENTEC 0x0537 Inventec vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG vendor SYNOPSYS 0x053f Synopsys vendor UNIACCESS 0x0540 Universal Access vendor VIEWSONIC 0x0543 ViewSonic vendor XIRLINK 0x0545 Xirlink vendor ANCHOR 0x0547 Anchor Chips vendor SONY 0x054c Sony vendor FUJIXEROX 0x0550 Fuji Xerox vendor VISION 0x0553 VLSI Vision vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems vendor ATEN 0x0557 ATEN International vendor SAMSUNG2 0x055d Samsung Electronics vendor MUSTEK 0x055f Mustek Systems vendor TELEX 0x0562 Telex Communications vendor CHINON 0x0564 Chinon vendor PERACOM 0x0565 Peracom Networks vendor ALCOR2 0x0566 Alcor Micro vendor XYRATEX 0x0567 Xyratex vendor WACOM 0x056a WACOM vendor ETEK 0x056c e-TEK Labs vendor EIZO 0x056d EIZO vendor ELECOM 0x056e Elecom vendor CONEXANT 0x0572 Conexant vendor HAUPPAUGE 0x0573 Hauppauge Computer Works vendor BAFO 0x0576 BAFO/Quality Computer Accessories vendor YEDATA 0x057b Y-E Data vendor AVM 0x057c AVM vendor QUICKSHOT 0x057f Quickshot vendor ROLAND 0x0582 Roland vendor ROCKFIRE 0x0583 Rockfire vendor RATOC 0x0584 RATOC Systems vendor ZYXEL 0x0586 ZyXEL Communication vendor INFINEON 0x058b Infineon vendor MICREL 0x058d Micrel vendor ALCOR 0x058f Alcor Micro vendor OMRON 0x0590 OMRON vendor ZORAN 0x0595 Zoran Microelectronics vendor NIIGATA 0x0598 Niigata vendor IOMEGA 0x059b Iomega vendor ATREND 0x059c A-Trend Technology vendor AID 0x059d Advanced Input Devices vendor LACIE 0x059f LaCie vendor FUJIFILM 0x05a2 Fuji Film vendor ARC 0x05a3 ARC vendor ORTEK 0x05a4 Ortek vendor CISCOLINKSYS3 0x05a6 Cisco-Linksys vendor BOSE 0x05a7 Bose vendor OMNIVISION 0x05a9 OmniVision vendor INSYSTEM 0x05ab In-System Design vendor APPLE 0x05ac Apple Computer vendor YCCABLE 0x05ad Y.C. Cable vendor DIGITALPERSONA 0x05ba DigitalPersona vendor 3G 0x05bc 3G Green Green Globe vendor RAFI 0x05bd RAFI vendor TYCO 0x05be Tyco vendor KAWASAKI 0x05c1 Kawasaki vendor DIGI 0x05c5 Digi International vendor QUALCOMM2 0x05c6 Qualcomm vendor QTRONIX 0x05c7 Qtronix vendor FOXLINK 0x05c8 Foxlink vendor RICOH 0x05ca Ricoh vendor ELSA 0x05cc ELSA vendor SCIWORX 0x05ce sci-worx vendor BRAINBOXES 0x05d1 Brainboxes Limited vendor ULTIMA 0x05d8 Ultima vendor AXIOHM 0x05d9 Axiohm Transaction Solutions vendor MICROTEK 0x05da Microtek vendor SUNTAC 0x05db SUN Corporation vendor LEXAR 0x05dc Lexar Media vendor ADDTRON 0x05dd Addtron vendor SYMBOL 0x05e0 Symbol Technologies vendor SYNTEK 0x05e1 Syntek vendor GENESYS 0x05e3 Genesys Logic vendor FUJI 0x05e5 Fuji Electric vendor KEITHLEY 0x05e6 Keithley Instruments vendor EIZONANAO 0x05e7 EIZO Nanao vendor KLSI 0x05e9 Kawasaki LSI vendor FFC 0x05eb FFC vendor ANKO 0x05ef Anko Electronic vendor PIENGINEERING 0x05f3 P.I. Engineering vendor AOC 0x05f6 AOC International vendor CHIC 0x05fe Chic Technology vendor BARCO 0x0600 Barco Display Systems vendor BRIDGE 0x0607 Bridge Information vendor SOLIDYEAR 0x060b Solid Year vendor BIORAD 0x0614 Bio-Rad Laboratories vendor MACALLY 0x0618 Macally vendor ACTLABS 0x061c Act Labs vendor ALARIS 0x0620 Alaris vendor APEX 0x0624 Apex vendor CREATIVE3 0x062a Creative Labs vendor MICRON 0x0634 Micron Technology vendor VIVITAR 0x0636 Vivitar vendor GUNZE 0x0637 Gunze Electronics USA vendor AVISION 0x0638 Avision vendor TEAC 0x0644 TEAC vendor ACTON 0x0647 Acton Research Corp. vendor OPTO 0x065a Optoelectronics Co., Ltd vendor SGI 0x065e Silicon Graphics vendor SANWASUPPLY 0x0663 Sanwa Supply vendor MEGATEC 0x0665 Megatec vendor LINKSYS 0x066b Linksys vendor ACERSA 0x066e Acer Semiconductor America vendor SIGMATEL 0x066f Sigmatel vendor DRAYTEK 0x0675 DrayTek vendor AIWA 0x0677 Aiwa vendor ACARD 0x0678 ACARD Technology vendor PROLIFIC 0x067b Prolific Technology vendor SIEMENS 0x067c Siemens vendor AVANCELOGIC 0x0680 Avance Logic vendor SIEMENS2 0x0681 Siemens vendor MINOLTA 0x0686 Minolta vendor CHPRODUCTS 0x068e CH Products vendor HAGIWARA 0x0693 Hagiwara Sys-Com vendor CTX 0x0698 Chuntex vendor ASKEY 0x069a Askey Computer vendor SAITEK 0x06a3 Saitek vendor ALCATELT 0x06b9 Alcatel Telecom vendor AGFA 0x06bd AGFA-Gevaert vendor ASIAMD 0x06be Asia Microelectronic Development vendor BIZLINK 0x06c4 Bizlink International vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. vendor CONTEC 0x06ce Contec products vendor AASHIMA 0x06d6 Aashima Technology vendor LIEBERT 0x06da Liebert vendor MULTITECH 0x06e0 MultiTech vendor ADS 0x06e1 ADS Technologies vendor ALCATELM 0x06e4 Alcatel Microelectronics vendor SIRIUS 0x06ea Sirius Technologies vendor GUILLEMOT 0x06f8 Guillemot vendor BOSTON 0x06fd Boston Acoustics vendor SMC 0x0707 Standard Microsystems vendor PUTERCOM 0x0708 Putercom vendor MCT 0x0711 MCT vendor IMATION 0x0718 Imation vendor TECLAST 0x071b Teclast vendor SONYERICSSON 0x0731 Sony Ericsson vendor EICON 0x0734 Eicon Networks vendor SYNTECH 0x0745 Syntech Information vendor DIGITALSTREAM 0x074e Digital Stream vendor AUREAL 0x0755 Aureal Semiconductor vendor MAUDIO 0x0763 M-Audio vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc. vendor SURECOM 0x0769 Surecom Technology vendor HIDGLOBAL 0x076b HID Global vendor LINKSYS2 0x077b Linksys vendor GRIFFIN 0x077d Griffin Technology vendor SANDISK 0x0781 SanDisk vendor JENOPTIK 0x0784 Jenoptik vendor LOGITEC 0x0789 Logitec vendor NOKIA2 0x078b Nokia vendor BRIMAX 0x078e Brimax vendor AXIS 0x0792 Axis Communications vendor ABL 0x0794 ABL Electronics vendor SAGEM 0x079b Sagem vendor SUNCOMM 0x079c Sun Communications, Inc. vendor ALFADATA 0x079d Alfadata Computer vendor NATIONALTECH 0x07a2 National Technical Systems vendor ONNTO 0x07a3 Onnto vendor BE 0x07a4 Be vendor ADMTEK 0x07a6 ADMtek vendor COREGA 0x07aa Corega vendor FREECOM 0x07ab Freecom vendor MICROTECH 0x07af Microtech vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) vendor OLYMPUS 0x07b4 Olympus vendor ABOCOM 0x07b8 AboCom Systems vendor KEISOKUGIKEN 0x07c1 Keisokugiken vendor ONSPEC 0x07c4 OnSpec vendor APG 0x07c5 APG Cash Drawer vendor BUG 0x07c8 B.U.G. vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International vendor AVERMEDIA 0x07ca AVerMedia Technologies vendor SIIG 0x07cc SIIG vendor CASIO 0x07cf CASIO vendor DLINK2 0x07d1 D-Link vendor APTIO 0x07d2 Aptio Products vendor ARASAN 0x07da Arasan Chip Systems vendor ALLIEDCABLE 0x07e6 Allied Cable vendor STSN 0x07ef STSN vendor CENTURY 0x07f7 Century Corp vendor NEWLINK 0x07ff NEWlink vendor MAGTEK 0x0801 Mag-Tek vendor ZOOM 0x0803 Zoom Telephonics vendor PCS 0x0810 Personal Communication Systems vendor ALPHASMART 0x081e AlphaSmart, Inc. vendor BROADLOGIC 0x0827 BroadLogic vendor HANDSPRING 0x082d Handspring vendor PALM 0x0830 Palm Computing vendor SOURCENEXT 0x0833 SOURCENEXT vendor ACTIONSTAR 0x0835 Action Star Enterprise vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin vendor ACCTON 0x083a Accton Technology vendor DIAMOND 0x0841 Diamond vendor NETGEAR 0x0846 BayNETGEAR vendor TOPRE 0x0853 Topre Corporation vendor ACTIVEWIRE 0x0854 ActiveWire vendor BBELECTRONICS 0x0856 B&B Electronics vendor PORTGEAR 0x085a PortGear vendor NETGEAR2 0x0864 Netgear vendor SYSTEMTALKS 0x086e System Talks vendor METRICOM 0x0870 Metricom vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America vendor JATON 0x087d Jaton vendor APT 0x0880 APT Technologies vendor BOCARESEARCH 0x0885 Boca Research vendor ANDREA 0x08a8 Andrea Electronics vendor BURRBROWN 0x08bb Burr-Brown Japan vendor 2WIRE 0x08c8 2Wire vendor AIPTEK 0x08ca AIPTEK International vendor SMARTBRIDGES 0x08d1 SmartBridges vendor FUJITSUSIEMENS 0x08d4 Fujitsu-Siemens vendor BILLIONTON 0x08dd Billionton Systems vendor GEMALTO 0x08e6 Gemalto SA vendor EXTENDED 0x08e9 Extended Systems vendor MSYSTEMS 0x08ec M-Systems vendor DIGIANSWER 0x08fd Digianswer vendor AUTHENTEC 0x08ff AuthenTec vendor AUDIOTECHNICA 0x0909 Audio-Technica vendor TRUMPION 0x090a Trumpion Microelectronics vendor FEIYA 0x090c Feiya vendor ALATION 0x0910 Alation Systems vendor GLOBESPAN 0x0915 Globespan vendor CONCORDCAMERA 0x0919 Concord Camera vendor GARMIN 0x091e Garmin International vendor GOHUBS 0x0921 GoHubs vendor DYMO 0x0922 DYMO vendor XEROX 0x0924 Xerox vendor BIOMETRIC 0x0929 American Biometric Company vendor TOSHIBA 0x0930 Toshiba vendor PLEXTOR 0x093b Plextor vendor INTREPIDCS 0x093c Intrepid vendor YANO 0x094f Yano vendor KINGSTON 0x0951 Kingston Technology vendor BLUEWATER 0x0956 BlueWater Systems vendor AGILENT 0x0957 Agilent Technologies vendor GUDE 0x0959 Gude ADS vendor PORTSMITH 0x095a Portsmith vendor ACERW 0x0967 Acer vendor ADIRONDACK 0x0976 Adirondack Wire & Cable vendor BECKHOFF 0x0978 Beckhoff vendor MINDSATWORK 0x097a Minds At Work vendor POINTCHIPS 0x09a6 PointChips vendor INTERSIL 0x09aa Intersil vendor ALTIUS 0x09b3 Altius Solutions vendor ARRIS 0x09c1 Arris Interactive vendor ACTIVCARD 0x09c3 ACTIVCARD vendor ACTISYS 0x09c4 ACTiSYS vendor NOVATEL2 0x09d7 Novatel Wireless vendor AFOURTECH 0x09da A-FOUR TECH vendor AIMEX 0x09dc AIMEX vendor ADDONICS 0x09df Addonics Technologies vendor AKAI 0x09e8 AKAI professional M.I. vendor ARESCOM 0x09f5 ARESCOM vendor BAY 0x09f9 Bay Associates vendor ALTERA 0x09fb Altera vendor CSR 0x0a12 Cambridge Silicon Radio vendor TREK 0x0a16 Trek Technology vendor ASAHIOPTICAL 0x0a17 Asahi Optical vendor BOCASYSTEMS 0x0a43 Boca Systems vendor SHANTOU 0x0a46 ShanTou vendor MEDIAGEAR 0x0a48 MediaGear vendor BROADCOM 0x0a5c Broadcom vendor GREENHOUSE 0x0a6b GREENHOUSE vendor MEDELI 0x0a67 Medeli vendor GEOCAST 0x0a79 Geocast Network Systems vendor EGO 0x0a92 EGO systems vendor IDQUANTIQUE 0x0aba ID Quantique vendor IDTECH 0x0acd ID TECH vendor ZYDAS 0x0ace Zydas Technology Corporation vendor NEODIO 0x0aec Neodio vendor OPTION 0x0af0 Option N.V. vendor ASUS 0x0b05 ASUSTeK Computer vendor TODOS 0x0b0c Todos Data System vendor SIIG2 0x0b39 SIIG vendor TEKRAM 0x0b3b Tekram Technology vendor HAL 0x0b41 HAL Corporation vendor EMS 0x0b43 EMS Production vendor NEC2 0x0b62 NEC vendor ADLINK 0x0b63 ADLINK Technoligy, Inc. vendor ATI2 0x0b6f ATI vendor ZEEVO 0x0b7a Zeevo, Inc. vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc. vendor SMART 0x0b8c Smart Technologies vendor ASIX 0x0b95 ASIX Electronics vendor O2MICRO 0x0b97 O2 Micro, Inc. vendor USR 0x0baf U.S. Robotics vendor AMBIT 0x0bb2 Ambit Microsystems vendor HTC 0x0bb4 HTC vendor REALTEK 0x0bda Realtek vendor ERICSSON2 0x0bdb Ericsson vendor MEI 0x0bed MEI vendor ADDONICS2 0x0bf6 Addonics Technology vendor FSC 0x0bf8 Fujitsu Siemens Computers vendor AGATE 0x0c08 Agate Technologies vendor DMI 0x0c0b DMI vendor CANYON 0x0c10 Canyon vendor ICOM 0x0c26 Icom Inc. vendor GNOTOMETRICS 0x0c33 GN Otometrics vendor CHICONY2 0x0c45 Chicony / Microdia / Sonix Technology Co., Ltd. vendor REINERSCT 0x0c4b Reiner-SCT vendor SEALEVEL 0x0c52 Sealevel System vendor JETI 0x0c6c Jeti vendor LUWEN 0x0c76 Luwen vendor ELEKTOR 0x0c7d ELEKTOR Electronics vendor KYOCERA2 0x0c88 Kyocera Wireless Corp. vendor ZCOM 0x0cde Z-Com vendor ATHEROS2 0x0cf3 Atheros Communications vendor POSIFLEX 0x0d3a POSIFLEX vendor TANGTOP 0x0d3d Tangtop vendor KOBIL 0x0d46 KOBIL vendor SMC3 0x0d5c Standard Microsystems vendor ADDON 0x0d7d Add-on Technology vendor ACDC 0x0d7e American Computer & Digital Components vendor CMEDIA 0x0d8c CMEDIA vendor CONCEPTRONIC 0x0d8e Conceptronic vendor SKANHEX 0x0d96 Skanhex Technology, Inc. vendor MSI 0x0db0 Micro Star International vendor ELCON 0x0db7 ELCON Systemtechnik vendor UNKNOWN4 0x0dcd Unknown vendor vendor NETAC 0x0dd8 Netac vendor SITECOMEU 0x0df6 Sitecom Europe vendor MOBILEACTION 0x0df7 Mobile Action vendor AMIGO 0x0e0b Amigo Technology vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia vendor HAWKING 0x0e66 Hawking vendor FOSSIL 0x0e67 Fossil, Inc vendor GMATE 0x0e7e G.Mate, Inc vendor MEDIATEK 0x0e8d MediaTek, Inc. vendor OTI 0x0ea0 Ours Technology vendor YISO 0x0eab Yiso Wireless Co. vendor PILOTECH 0x0eaf Pilotech vendor NOVATECH 0x0eb0 NovaTech vendor ITEGNO 0x0eba iTegno vendor WINMAXGROUP 0x0ed1 WinMaxGroup vendor TOD 0x0ede TOD vendor EGALAX 0x0eef eGalax, Inc. vendor AIRPRIME 0x0f3d AirPrime, Inc. vendor MICROTUNE 0x0f4d Microtune vendor VTECH 0x0f88 VTech vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH vendor RIM 0x0fca Research In Motion vendor DYNASTREAM 0x0fcf Dynastream Innovations vendor LARSENBRUSGAARD 0x0fd8 Larsen and Brusgaard vendor OWL 0x0fde OWL vendor KONTRON 0x0fe6 Kontron AG vendor QUALCOMM 0x1004 Qualcomm vendor APACER 0x1005 Apacer vendor MOTOROLA4 0x100d Motorola vendor HP3 0x103c Hewlett Packard vendor AIRPLUS 0x1011 Airplus vendor DESKNOTE 0x1019 Desknote vendor NEC3 0x1033 NEC vendor TTI 0x103e Thurlby Thandar Instruments vendor GIGABYTE 0x1044 GIGABYTE vendor WESTERN 0x1058 Western Digital vendor MOTOROLA 0x1063 Motorola vendor CCYU 0x1065 CCYU Technology vendor CURITEL 0x106c Curitel Communications Inc vendor SILABS2 0x10a6 SILABS2 vendor USI 0x10ab USI vendor LIEBERT2 0x10af Liebert vendor PLX 0x10b5 PLX vendor ASANTE 0x10bd Asante vendor SILABS 0x10c4 Silicon Labs vendor SILABS3 0x10c5 Silicon Labs vendor SILABS4 0x10ce Silicon Labs vendor ACTIONS 0x10d6 Actions vendor ANALOG 0x1110 Analog Devices vendor TENX 0x1130 Ten X Technology, Inc. vendor ISSC 0x1131 Integrated System Solution Corp. vendor JRC 0x1145 Japan Radio Company vendor SPHAIRON 0x114b Sphairon Access Systems GmbH vendor DELORME 0x1163 DeLorme vendor SERVERWORKS 0x1166 ServerWorks vendor DLINK3 0x1186 Dlink vendor ACERCM 0x1189 Acer Communications & Multimedia vendor SIERRA 0x1199 Sierra Wireless vendor SANWA 0x11ad Sanwa Electric Instrument Co., Ltd. vendor TOPFIELD 0x11db Topfield Co., Ltd vendor SIEMENS3 0x11f5 Siemens vendor NETINDEX 0x11f6 NetIndex vendor ALCATEL 0x11f7 Alcatel vendor INTERBIOMETRICS 0x1209 Interbiometrics vendor UNKNOWN3 0x1233 Unknown vendor vendor TSUNAMI 0x1241 Tsunami vendor PHEENET 0x124a Pheenet vendor TARGUS 0x1267 Targus vendor TWINMOS 0x126f TwinMOS vendor TENDA 0x1286 Tenda vendor TESTO 0x128d Testo products vendor CREATIVE2 0x1292 Creative Labs vendor BELKIN2 0x1293 Belkin Components vendor CYBERTAN 0x129b CyberTAN Technology vendor HUAWEI 0x12d1 Huawei Technologies vendor ARANEUS 0x12d8 Araneus Information Systems vendor TAPWAVE 0x12ef Tapwave vendor AINCOMM 0x12fd Aincomm vendor MOBILITY 0x1342 Mobility vendor DICKSMITH 0x1371 Dick Smith Electronics vendor NETGEAR3 0x1385 Netgear vendor BALTECH 0x13ad Baltech vendor CISCOLINKSYS 0x13b1 Cisco-Linksys vendor SHARK 0x13d2 Shark vendor AZUREWAVE 0x13d3 AsureWave vendor INITIO 0x13fd Initio Corporation vendor EMTEC 0x13fe Emtec vendor NOVATEL 0x1410 Novatel Wireless vendor MERLIN 0x1416 Merlin vendor REDOCTANE 0x1430 RedOctane vendor WISTRONNEWEB 0x1435 Wistron NeWeb vendor RADIOSHACK 0x1453 Radio Shack vendor FIC 0x1457 FIC / OpenMoko vendor HUAWEI3COM 0x1472 Huawei-3Com vendor ABOCOM2 0x1482 AboCom Systems vendor SILICOM 0x1485 Silicom vendor RALINK 0x148f Ralink Technology vendor IMAGINATION 0x149a Imagination Technologies vendor ATP 0x14af ATP Electronics vendor CONCEPTRONIC2 0x14b2 Conceptronic vendor SUPERTOP 0x14cd Super Top vendor PLANEX3 0x14ea Planex Communications vendor SILICONPORTALS 0x1527 Silicon Portals vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd. vendor JMICRON 0x152d JMicron vendor UBLOX 0x1546 U-blox vendor PNY 0x154b PNY vendor OWEN 0x1555 Owen vendor OQO 0x1557 OQO vendor UMEDIA 0x157e U-MEDIA Communications vendor FIBERLINE 0x1582 Fiberline vendor FREESCALE 0x15a2 Freescale Semiconductor, Inc. vendor AFATECH 0x15a4 Afatech Technologies, Inc. vendor SPARKLAN 0x15a9 SparkLAN vendor OLIMEX 0x15ba Olimex vendor SOUNDGRAPH 0x15c2 Soundgraph, Inc. vendor AMIT2 0x15c5 AMIT vendor TEXTECH 0x15ca Textech International Ltd. vendor SOHOWARE 0x15e8 SOHOware vendor UMAX 0x1606 UMAX Data Systems vendor INSIDEOUT 0x1608 Inside Out Networks vendor AMOI 0x1614 Amoi Electronics vendor GOODWAY 0x1631 Good Way Technology vendor ENTREGA 0x1645 Entrega vendor ACTIONTEC 0x1668 Actiontec Electronics vendor CLIPSAL 0x166a Clipsal vendor CISCOLINKSYS2 0x167b Cisco-Linksys vendor ATHEROS 0x168c Atheros Communications vendor GIGASET 0x1690 Gigaset vendor GLOBALSUN 0x16ab Global Sun Technology vendor ANYDATA 0x16d5 AnyDATA Corporation vendor JABLOTRON 0x16d6 Jablotron vendor CMOTECH 0x16d8 C-motech vendor WIENERPLEINBAUS 0x16dc WIENER Plein & Baus GmbH. vendor AXESSTEL 0x1726 Axesstel Co., Ltd. vendor LINKSYS4 0x1737 Linksys vendor SENAO 0x1740 Senao vendor ASUS2 0x1761 ASUS vendor SWEEX2 0x177f Sweex vendor METAGEEK 0x1781 MetaGeek vendor KAMSTRUP 0x17a8 Kamstrup A/S vendor DISPLAYLINK 0x17e9 DisplayLink vendor LENOVO 0x17ef Lenovo vendor WAVESENSE 0x17f4 WaveSense vendor VAISALA 0x1843 Vaisala vendor AMIT 0x18c5 AMIT vendor GOOGLE 0x18d1 Google vendor QCOM 0x18e8 Qcom vendor ELV 0x18ef ELV vendor LINKSYS3 0x1915 Linksys vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated vendor QUALCOMM3 0x19f5 Qualcomm, Inc. vendor BAYER 0x1a79 Bayer vendor WCH2 0x1a86 QinHeng Electronics vendor STELERA 0x1a8d Stelera Wireless vendor SEL 0x1adb Schweitzer Engineering Laboratories vendor CORSAIR 0x1b1c Corsair vendor MATRIXORBITAL 0x1b3d Matrix Orbital vendor OVISLINK 0x1b75 OvisLink vendor TML 0x1b91 The Mobility Lab vendor TCTMOBILE 0x1bbb TCT Mobile vendor ALTI2 0x1bc9 Alti-2 products vendor SUNPLUS 0x1bcf Sunplus Innovation Technology Inc. vendor WAGO 0x1be3 WAGO Kontakttechnik GmbH. vendor TELIT 0x1bc7 Telit vendor IONICS 0x1c0c Ionics PlugComputer vendor LONGCHEER 0x1c9e Longcheer Holdings, Ltd. vendor MPMAN 0x1cae MpMan vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik vendor NEOTEL 0x1d09 Neotel vendor DREAMLINK 0x1d34 Dream Link vendor PEGATRON 0x1d4d Pegatron vendor QISDA 0x1da5 Qisda vendor METAGEEK2 0x1dd5 MetaGeek vendor ALINK 0x1e0e Alink vendor AIRTIES 0x1eda AirTies vendor FESTO 0x1e29 Festo vendor LAKESHORE 0x1fb9 Lake Shore Cryotronics, Inc. vendor VERTEX 0x1fe7 Vertex Wireless Co., Ltd. vendor DLINK 0x2001 D-Link vendor PLANEX2 0x2019 Planex Communications vendor HAUPPAUGE2 0x2040 Hauppauge Computer Works vendor TLAYTECH 0x20b9 Tlay Tech vendor ENCORE 0x203d Encore vendor QIHARDWARE 0x20b7 QI-hardware vendor PARA 0x20b8 PARA Industrial vendor SIMTEC 0x20df Simtec Electronics vendor TRENDNET 0x20f4 TRENDnet vendor RTSYSTEMS 0x2100 RTSYSTEMS vendor VIALABS 0x2109 VIA Labs vendor ERICSSON 0x2282 Ericsson vendor MOTOROLA2 0x22b8 Motorola vendor WETELECOM 0x22de WeTelecom vendor WESTMOUNTAIN 0x2405 West Mountain Radio vendor TRIPPLITE 0x2478 Tripp-Lite vendor HIROSE 0x2631 Hirose Electric vendor NHJ 0x2770 NHJ vendor PLANEX 0x2c02 Planex Communications vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd vendor LINKINSTRUMENTS 0x3195 Link Instruments Inc. vendor AEI 0x3334 AEI vendor HANK 0x3353 Hank Connection vendor PQI 0x3538 PQI vendor DAISY 0x3579 Daisy Technology vendor NI 0x3923 National Instruments vendor MICRONET 0x3980 Micronet Communications vendor IODATA2 0x40bb I-O Data vendor IRIVER 0x4102 iRiver vendor DELL 0x413c Dell vendor WCH 0x4348 QinHeng Electronics vendor ACEECA 0x4766 Aceeca vendor FEIXUN 0x4855 FeiXun Communication vendor PAPOUCH 0x5050 Papouch products vendor AVERATEC 0x50c2 Averatec vendor SWEEX 0x5173 Sweex vendor PROLIFIC2 0x5372 Prolific Technologies vendor ONSPEC2 0x55aa OnSpec Electronic Inc. vendor ZINWELL 0x5a57 Zinwell vendor SITECOM 0x6189 Sitecom vendor ARKMICRO 0x6547 Arkmicro Technologies Inc. vendor 3COM2 0x6891 3Com vendor EDIMAX 0x7392 Edimax vendor INTEL 0x8086 Intel vendor INTEL2 0x8087 Intel vendor ALLWIN 0x8516 ALLWIN Tech vendor SITECOM2 0x9016 Sitecom vendor MOSCHIP 0x9710 MosChip Semiconductor vendor NETGEAR4 0x9846 Netgear vendor MARVELL 0x9e88 Marvell Technology Group Ltd. vendor 3COM3 0xa727 3Com vendor CACE 0xcace CACE Technologies vendor EVOLUTION 0xdeee Evolution Robotics products vendor DATAAPEX 0xdaae DataApex vendor HP2 0xf003 Hewlett Packard vendor LOGILINK 0xfc08 LogiLink vendor USRP 0xfffe GNU Radio USRP /* * List of known products. Grouped by vendor. */ /* 3Com products */ product 3COM HOMECONN 0x009d HomeConnect Camera product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter product 3COM 3C460 0x11f8 HomeConnect 3C460 product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro product 3COM 3C460B 0x4601 HomeConnect 3C460B product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 product 3COM3 AR5523_1 0x6893 AR5523 product 3COM3 AR5523_2 0x6895 AR5523 product 3COM3 AR5523_3 0x6897 AR5523 product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro /* AboCom products */ product ABOCOM XX1 0x110c XX1 product ABOCOM XX2 0x200c XX2 product ABOCOM RT2770 0x2770 RT2770 product ABOCOM RT2870 0x2870 RT2870 product ABOCOM RT3070 0x3070 RT3070 product ABOCOM RT3071 0x3071 RT3071 product ABOCOM RT3072 0x3072 RT3072 product ABOCOM2 RT2870_1 0x3c09 RT2870 product ABOCOM URE450 0x4000 URE450 Ethernet Adapter product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter product ABOCOM XX4 0x4004 XX4 product ABOCOM XX5 0x4007 XX5 product ABOCOM XX6 0x400b XX6 product ABOCOM XX7 0x400c XX7 product ABOCOM RTL8151 0x401a RTL8151 product ABOCOM XX8 0x4102 XX8 product ABOCOM XX9 0x4104 XX9 product ABOCOM UF200 0x420a UF200 Ethernet product ABOCOM WL54 0x6001 WL54 product ABOCOM XX10 0xabc1 XX10 product ABOCOM BWU613 0xb000 BWU613 product ABOCOM HWU54DM 0xb21b HWU54DM product ABOCOM RT2573_2 0xb21c RT2573 product ABOCOM RT2573_3 0xb21d RT2573 product ABOCOM RT2573_4 0xb21e RT2573 product ABOCOM RTL8188CU_1 0x8188 RTL8188CU product ABOCOM RTL8188CU_2 0x8189 RTL8188CU product ABOCOM RTL8192CU 0x8178 RTL8192CU product ABOCOM WUG2700 0xb21f WUG2700 /* Acton Research Corp. */ product ACTON SPECTRAPRO 0x0100 FTDI compatible adapter /* Accton products */ product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter product ACCTON 2664W 0x3501 2664W product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter product ACCTON SMCWUSBG_NF 0x4505 SMCWUSB-G (no firmware) product ACCTON SMCWUSBG 0x4506 SMCWUSB-G product ACCTON SMCWUSBTG2_NF 0x4507 SMCWUSBT-G2 (no firmware) product ACCTON SMCWUSBTG2 0x4508 SMCWUSBT-G2 product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter product ACCTON RT2870_2 0x6618 RT2870 product ACCTON RT3070 0x7511 RT3070 product ACCTON RT2770 0x7512 RT2770 product ACCTON RT2870_3 0x7522 RT2870 product ACCTON RT2870_5 0x8522 RT2870 product ACCTON RT3070_4 0xa512 RT3070 product ACCTON RT2870_4 0xa618 RT2870 product ACCTON RT3070_1 0xa701 RT3070 product ACCTON RT3070_2 0xa702 RT3070 product ACCTON RT2870_1 0xb522 RT2870 product ACCTON RT3070_3 0xc522 RT3070 product ACCTON RT3070_5 0xd522 RT3070 product ACCTON RTL8192SU 0xc512 RTL8192SU product ACCTON ZD1211B 0xe501 ZD1211B product ACCTON WN7512 0xf522 WN7512 /* Aceeca products */ product ACEECA MEZ1000 0x0001 MEZ1000 RDA /* Acer Communications & Multimedia (oemd by Surecom) */ product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter /* Acer Labs products */ product ACERLABS M5632 0x5632 USB 2.0 Data Link /* Acer Peripherals, Inc. products */ product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U product ACERP ACERSCAN_320U 0x2022 Acerscan 320U product ACERP ACERSCAN_640U 0x2040 Acerscan 640U product ACERP ACERSCAN_620U 0x2060 Acerscan 620U product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U product ACERP S81 0x4027 BenQ S81 phone product ACERP H10 0x4068 AWL400 Wireless Adapter product ACERP ATAPI 0x6003 ATA/ATAPI Adapter product ACERP AWL300 0x9000 AWL300 Wireless Adapter product ACERP AWL400 0x9001 AWL400 Wireless Adapter /* Acer Warp products */ product ACERW WARPLINK 0x0204 Warplink /* Actions products */ product ACTIONS MP4 0x1101 Actions MP4 Player /* Actiontec, Inc. products */ product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter /* ACTiSYS products */ product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR /* ActiveWire, Inc. products */ product ACTIVEWIRE IOBOARD 0x0100 I/O Board product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware /* Adaptec products */ product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN /* Addtron products */ product ADDTRON AWU120 0xff31 AWU-120 /* ADLINK Texhnology products */ product ADLINK ND6530 0x6530 ND-6530 USB-Serial /* ADMtek products */ product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet product ADMTEK PEGASUS 0x0986 AN986 Ethernet product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet /* ADDON products */ /* PNY OEMs these */ product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) /* Addonics products */ product ADDONICS2 CABLE_205 0xa001 Cable 205 /* ADS products */ product ADS UBS10BT 0x0008 UBS-10BT Ethernet product ADS UBS10BTX 0x0009 UBS-10BT Ethernet /* AEI products */ product AEI FASTETHERNET 0x1701 Fast Ethernet /* Afatech Technologies, Inc. */ product AFATECH AFATECH1336 0x1336 Flash Card Reader /* Agate Technologies products */ product AGATE QDRIVE 0x0378 Q-Drive /* AGFA products */ product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U product AGFA SNAPSCANE40 0x208d SnapScan e40 product AGFA SNAPSCANE50 0x208f SnapScan e50 product AGFA SNAPSCANE20 0x2091 SnapScan e20 product AGFA SNAPSCANE25 0x2095 SnapScan e25 product AGFA SNAPSCANE26 0x2097 SnapScan e26 product AGFA SNAPSCANE52 0x20fd SnapScan e52 /* Ain Communication Technology products */ product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter /* AIPTEK products */ product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3 product AIPTEK2 SUNPLUS_TECH 0x0c15 Sunplus Technology Inc. /* AirPlis products */ product AIRPLUS MCD650 0x3198 MCD650 modem /* AirPrime products */ product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card product AIRPRIME USB308 0x68A3 USB308 HSPA+ USB Modem product AIRPRIME AC313U 0x68aa Sierra Wireless AirCard 313U /* AirTies products */ product AIRTIES RT3070 0x2310 RT3070 /* AKS products */ product AKS USBHASP 0x0001 USB-HASP 0.06 /* Alcatel products */ product ALCATEL OT535 0x02df One Touch 535/735 /* Alcor Micro, Inc. products */ product ALCOR2 KBD_HUB 0x2802 Kbd Hub product ALCOR DUMMY 0x0000 Dummy product product ALCOR SDCR_6335 0x6335 SD/MMC Card Reader product ALCOR SDCR_6362 0x6362 SD/MMC Card Reader product ALCOR SDCR_6366 0x6366 SD/MMC Card Reader product ALCOR TRANSCEND 0x6387 Transcend JetFlash Drive product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub product ALCOR AU9814 0x9215 AU9814 Hub product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub product ALCOR AU9720 0x9720 USB2 - RS-232 product ALCOR AU6390 0x6390 AU6390 USB-IDE converter /* Alink products */ product ALINK DWM652U5 0xce16 DWM-652 product ALINK 3G 0x9000 3G modem product ALINK 3GU 0x9200 3G modem /* Altec Lansing products */ product ALTEC ADA70 0x0070 ADA70 Speakers product ALTEC ASC495 0xff05 ASC495 Speakers /* Alti-2 products */ product ALTI2 N3 0x6001 FTDI compatible adapter /* Allied Telesyn International products */ product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100 /* ALLWIN Tech products */ product ALLWIN RT2070 0x2070 RT2070 product ALLWIN RT2770 0x2770 RT2770 product ALLWIN RT2870 0x2870 RT2870 product ALLWIN RT3070 0x3070 RT3070 product ALLWIN RT3071 0x3071 RT3071 product ALLWIN RT3072 0x3072 RT3072 product ALLWIN RT3572 0x3572 RT3572 /* AlphaSmart, Inc. products */ product ALPHASMART DANA_KB 0xdbac AlphaSmart Dana Keyboard product ALPHASMART DANA_SYNC 0xdf00 AlphaSmart Dana HotSync /* Amoi products */ product AMOI H01 0x0800 H01 3G modem product AMOI H01A 0x7002 H01A 3G modem product AMOI H02 0x0802 H02 3G modem /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Ambit Microsystems products */ product AMBIT WLAN 0x0302 WLAN product AMBIT NTL_250 0x6098 NTL 250 cable modem /* Apacer products */ product APACER HT202 0xb113 USB 2.0 Flash Drive /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Amigo Technology products */ product AMIGO RT2870_1 0x9031 RT2870 product AMIGO RT2870_2 0x9041 RT2870 /* AMIT products */ product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO product AMIT CGWLUSB2GNR 0x0008 CG-WLUSB2GNR product AMIT RT2870_1 0x0012 RT2870 /* AMIT(2) products */ product AMIT2 RT2870 0x0008 RT2870 /* Analog Devices products */ product ANALOGDEVICES GNICE 0xf000 FTDI compatible adapter product ANALOGDEVICES GNICEPLUS 0xf001 FTDI compatible adapter /* Anchor products */ product ANCHOR SERIAL 0x2008 Serial product ANCHOR EZUSB 0x2131 EZUSB product ANCHOR EZLINK 0x2720 EZLINK /* AnyData products */ product ANYDATA ADU_620UW 0x6202 CDMA 2000 EV-DO USB Modem product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem /* AOX, Inc. products */ product AOX USB101 0x0008 Ethernet /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Apple Computer products */ product APPLE DUMMY 0x0000 Dummy product product APPLE IMAC_KBD 0x0201 USB iMac Keyboard product APPLE KBD 0x0202 USB Keyboard M2452 product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard /* MacbookAir, aka wellspring */ product APPLE WELLSPRING_ANSI 0x0223 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_ISO 0x0224 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING_JIS 0x0225 Apple Internal Keyboard/Trackpad /* MacbookProPenryn, aka wellspring2 */ product APPLE WELLSPRING2_ANSI 0x0230 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_ISO 0x0231 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING2_JIS 0x0232 Apple Internal Keyboard/Trackpad /* Macbook5,1 (unibody), aka wellspring3 */ product APPLE WELLSPRING3_ANSI 0x0236 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_ISO 0x0237 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING3_JIS 0x0238 Apple Internal Keyboard/Trackpad /* MacbookAir3,2 (unibody), aka wellspring4 */ product APPLE WELLSPRING4_ANSI 0x023f Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_ISO 0x0240 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4_JIS 0x0241 Apple Internal Keyboard/Trackpad /* MacbookAir3,1 (unibody), aka wellspring4 */ product APPLE WELLSPRING4A_ANSI 0x0242 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_ISO 0x0243 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING4A_JIS 0x0244 Apple Internal Keyboard/Trackpad /* Macbook8 (unibody, March 2011) */ product APPLE WELLSPRING5_ANSI 0x0245 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_ISO 0x0246 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5_JIS 0x0247 Apple Internal Keyboard/Trackpad /* MacbookAir4,1 (unibody, July 2011) */ product APPLE WELLSPRING6A_ANSI 0x0249 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_ISO 0x024a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6A_JIS 0x024b Apple Internal Keyboard/Trackpad /* MacbookAir4,2 (unibody, July 2011) */ product APPLE WELLSPRING6_ANSI 0x024c Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_ISO 0x024d Apple Internal Keyboard/Trackpad product APPLE WELLSPRING6_JIS 0x024e Apple Internal Keyboard/Trackpad /* Macbook8,2 (unibody) */ product APPLE WELLSPRING5A_ANSI 0x0252 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_ISO 0x0253 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING5A_JIS 0x0254 Apple Internal Keyboard/Trackpad /* MacbookPro10,1 (unibody, June 2012) */ product APPLE WELLSPRING7_ANSI 0x0262 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_ISO 0x0263 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7_JIS 0x0264 Apple Internal Keyboard/Trackpad /* MacbookPro10,2 (unibody, October 2012) */ product APPLE WELLSPRING7A_ANSI 0x0259 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_ISO 0x025a Apple Internal Keyboard/Trackpad product APPLE WELLSPRING7A_JIS 0x025b Apple Internal Keyboard/Trackpad /* MacbookAir6,2 (unibody, June 2013) */ product APPLE WELLSPRING8_ANSI 0x0290 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_ISO 0x0291 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING8_JIS 0x0292 Apple Internal Keyboard/Trackpad /* MacbookPro12,1 */ product APPLE WELLSPRING9_ANSI 0x0272 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_ISO 0x0273 Apple Internal Keyboard/Trackpad product APPLE WELLSPRING9_JIS 0x0274 Apple Internal Keyboard/Trackpad product APPLE MOUSE 0x0301 Mouse M4848 product APPLE OPTMOUSE 0x0302 Optical mouse product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse product APPLE KBD_HUB 0x1001 Hub in Apple USB Keyboard product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard product APPLE SPEAKERS 0x1101 Speakers product APPLE IPOD 0x1201 iPod product APPLE IPOD2G 0x1202 iPod 2G product APPLE IPOD3G 0x1203 iPod 3G product APPLE IPOD_04 0x1204 iPod '04' product APPLE IPODMINI 0x1205 iPod Mini product APPLE IPOD_06 0x1206 iPod '06' product APPLE IPOD_07 0x1207 iPod '07' product APPLE IPOD_08 0x1208 iPod '08' product APPLE IPODVIDEO 0x1209 iPod Video product APPLE IPODNANO 0x120a iPod Nano product APPLE IPHONE 0x1290 iPhone product APPLE IPOD_TOUCH 0x1291 iPod Touch product APPLE IPHONE_3G 0x1292 iPhone 3G product APPLE IPHONE_3GS 0x1294 iPhone 3GS product APPLE IPHONE_4 0x1297 iPhone 4 product APPLE IPHONE_4S 0x12a0 iPhone 4S product APPLE IPHONE_5 0x12a8 iPhone 5 product APPLE IPAD 0x129a iPad product APPLE ETHERNET 0x1402 Ethernet A1277 /* Arkmicro Technologies */ product ARKMICRO ARK3116 0x0232 ARK3116 Serial /* Asahi Optical products */ product ASAHIOPTICAL OPTIO230 0x0004 Digital camera product ASAHIOPTICAL OPTIO330 0x0006 Digital camera /* Asante products */ product ASANTE EA 0x1427 Ethernet /* ASIX Electronics products */ product ASIX AX88172 0x1720 10/100 Ethernet product ASIX AX88178 0x1780 AX88178 product ASIX AX88178A 0x178a AX88178A USB 2.0 10/100/1000 Ethernet product ASIX AX88179 0x1790 AX88179 USB 3.0 10/100/1000 Ethernet product ASIX AX88772 0x7720 AX88772 product ASIX AX88772A 0x772a AX88772A USB 2.0 10/100 Ethernet product ASIX AX88772B 0x772b AX88772B USB 2.0 10/100 Ethernet product ASIX AX88772B_1 0x7e2b AX88772B USB 2.0 10/100 Ethernet /* ASUS products */ product ASUS2 USBN11 0x0b05 USB-N11 product ASUS RT2570 0x1706 RT2500USB Wireless Adapter product ASUS WL167G 0x1707 WL-167g Wireless Adapter product ASUS WL159G 0x170c WL-159g product ASUS A9T_WIFI 0x171b A9T wireless product ASUS P5B_WIFI 0x171d P5B wireless product ASUS RT2573_1 0x1723 RT2573 product ASUS RT2573_2 0x1724 RT2573 product ASUS LCM 0x1726 LCM display product ASUS RT2870_1 0x1731 RT2870 product ASUS RT2870_2 0x1732 RT2870 product ASUS RT2870_3 0x1742 RT2870 product ASUS RT2870_4 0x1760 RT2870 product ASUS RT2870_5 0x1761 RT2870 product ASUS USBN13 0x1784 USB-N13 product ASUS USBN10 0x1786 USB-N10 product ASUS RT3070_1 0x1790 RT3070 product ASUS RTL8192SU 0x1791 RTL8192SU product ASUS USB_N53 0x179d ASUS Black Diamond Dual Band USB-N53 product ASUS RTL8192CU 0x17ab RTL8192CU product ASUS USBN66 0x17ad USB-N66 product ASUS USBN10NANO 0x17ba USB-N10 Nano product ASUS USBAC51 0x17d1 USB-AC51 product ASUS A730W 0x4202 ASUS MyPal A730W product ASUS P535 0x420f ASUS P535 PDA product ASUS GMSC 0x422f ASUS Generic Mass Storage /* ATen products */ product ATEN UC1284 0x2001 Parallel printer product ATEN UC10T 0x2002 10Mbps Ethernet product ATEN UC110T 0x2007 UC-110T Ethernet product ATEN UC232A 0x2008 Serial product ATEN UC210T 0x2009 UC-210T Ethernet product ATEN DSB650C 0x4000 DSB-650C /* ATP Electronics products */ product ATP EUSB 0xaf01 ATP IG eUSB SSD /* Atheros Communications products */ product ATHEROS AR5523 0x0001 AR5523 product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_1 0x0001 AR5523 product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_2 0x0003 AR5523 product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware) product ATHEROS2 AR5523_3 0x0005 AR5523 product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware) product ATHEROS2 TG121N 0x1001 TG121N product ATHEROS2 WN821NV2 0x1002 WN821NV2 product ATHEROS2 3CRUSBN275 0x1010 3CRUSBN275 product ATHEROS2 WN612 0x1011 WN612 product ATHEROS2 AR9170 0x9170 AR9170 /* Atmel Comp. products */ product ATMEL STK541 0x2109 Zigbee Controller product ATMEL UHB124 0x3301 UHB124 hub product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter product ATMEL BW002 0x7605 BW002 Wireless Adapter product ATMEL WL1130USB 0x7613 WL-1130 USB product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter /* AuthenTec products */ product AUTHENTEC AES1610 0x1600 AES1610 Fingerprint Sensor /* Avision products */ product AVISION 1200U 0x0268 1200U scanner /* AVM products */ product AVM FRITZWLAN 0x8401 FRITZ!WLAN N /* Axesstel products */ product AXESSTEL DATAMODEM 0x1000 Data Modem /* AsureWave products */ product AZUREWAVE RT2870_1 0x3247 RT2870 product AZUREWAVE RT2870_2 0x3262 RT2870 product AZUREWAVE RT3070_1 0x3273 RT3070 product AZUREWAVE RT3070_2 0x3284 RT3070 product AZUREWAVE RT3070_3 0x3305 RT3070 product AZUREWAVE RTL8188CU 0x3357 RTL8188CU product AZUREWAVE RTL8188CE_1 0x3358 RTL8188CE product AZUREWAVE RTL8188CE_2 0x3359 RTL8188CE product AZUREWAVE RTL8192SU_1 0x3306 RTL8192SU product AZUREWAVE RTL8192SU_2 0x3309 RTL8192SU product AZUREWAVE RTL8192SU_3 0x3310 RTL8192SU product AZUREWAVE RTL8192SU_4 0x3311 RTL8192SU product AZUREWAVE RTL8192SU_5 0x3325 RTL8192SU /* Baltech products */ product BALTECH CARDREADER 0x9999 Card reader /* Bayer products */ product BAYER CONTOUR_CABLE 0x6001 FTDI compatible adapter /* B&B Electronics products */ product BBELECTRONICS USOTL4 0xAC01 RS-422/485 product BBELECTRONICS 232USB9M 0xac27 FTDI compatible adapter product BBELECTRONICS 485USB9F_2W 0xac25 FTDI compatible adapter product BBELECTRONICS 485USB9F_4W 0xac26 FTDI compatible adapter product BBELECTRONICS 485USBTB_2W 0xac33 FTDI compatible adapter product BBELECTRONICS 485USBTB_4W 0xac34 FTDI compatible adapter product BBELECTRONICS TTL3USB9M 0xac50 FTDI compatible adapter product BBELECTRONICS TTL5USB9M 0xac49 FTDI compatible adapter product BBELECTRONICS USO9ML2 0xac03 FTDI compatible adapter product BBELECTRONICS USO9ML2DR 0xac17 FTDI compatible adapter product BBELECTRONICS USO9ML2DR_2 0xac16 FTDI compatible adapter product BBELECTRONICS USOPTL4 0xac11 FTDI compatible adapter product BBELECTRONICS USOPTL4DR 0xac19 FTDI compatible adapter product BBELECTRONICS USOPTL4DR2 0xac18 FTDI compatible adapter product BBELECTRONICS USPTL4 0xac12 FTDI compatible adapter product BBELECTRONICS USTL4 0xac02 FTDI compatible adapter product BBELECTRONICS ZZ_PROG1_USB 0xba02 FTDI compatible adapter /* Belkin products */ /*product BELKIN F5U111 0x???? F5U111 Ethernet*/ product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth product BELKIN F5U103 0x0103 F5U103 Serial product BELKIN F5U109 0x0109 F5U109 Serial product BELKIN USB2SCSI 0x0115 USB to SCSI product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter product BELKIN USB2LAN 0x0121 USB to LAN product BELKIN F5U208 0x0208 F5U208 VideoBus II product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub product BELKIN F5U257 0x0257 F5U257 Serial product BELKIN F5U409 0x0409 F5U409 Serial product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS product BELKIN F5U120 0x1203 F5U120-PC Hub product BELKIN RTL8188CU 0x1102 RTL8188CU Wireless Adapter product BELKIN F9L1103 0x1103 F9L1103 Wireless Adapter product BELKIN RTL8192CU 0x2102 RTL8192CU Wireless Adapter product BELKIN F7D2102 0x2103 F7D2102 Wireless Adapter product BELKIN ZD1211B 0x4050 ZD1211B product BELKIN F5D5055 0x5055 F5D5055 product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter /* Also sold as 'Ativa 802.11g wireless card' */ product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter product BELKIN F5D7050E 0x705e F5D7050E Wireless Adapter product BELKIN RT2870_1 0x8053 RT2870 product BELKIN RT2870_2 0x805c RT2870 product BELKIN F5D8053V3 0x815c F5D8053 v3 product BELKIN RTL8192SU_1 0x815f RTL8192SU product BELKIN RTL8192SU_2 0x845a RTL8192SU product BELKIN RTL8192SU_3 0x945a RTL8192SU product BELKIN F5D8055 0x825a F5D8055 product BELKIN F5D8055V2 0x825b F5D8055 v2 product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter product BELKIN2 F5U002 0x0002 F5U002 Parallel printer product BELKIN F6D4050V1 0x935a F6D4050 v1 product BELKIN F6D4050V2 0x935b F6D4050 v2 /* Billionton products */ product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet product BILLIONTON USBLP100 0x0987 USB100LP product BILLIONTON USBEL100 0x0988 USB100EL product BILLIONTON USBE100 0x8511 USBE100 product BILLIONTON USB2AR 0x90ff USB2AR Ethernet /* Broadcom products */ product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle /* Brother Industries products */ product BROTHER HL1050 0x0002 HL-1050 laser printer product BROTHER MFC8600_9650 0x0100 MFC8600/9650 multifunction device /* Behavior Technology Computer products */ product BTC BTC6100 0x5550 6100C Keyboard product BTC BTC7932 0x6782 Keyboard with mouse port /* CACE Technologies products */ product CACE AIRPCAPNX 0x0300 AirPcap NX /* Canon, Inc. products */ product CANON N656U 0x2206 CanoScan N656U product CANON N1220U 0x2207 CanoScan N1220U product CANON D660U 0x2208 CanoScan D660U product CANON N676U 0x220d CanoScan N676U product CANON N1240U 0x220e CanoScan N1240U product CANON LIDE25 0x2220 CanoScan LIDE 25 product CANON S10 0x3041 PowerShot S10 product CANON S100 0x3045 PowerShot S100 product CANON S200 0x3065 PowerShot S200 product CANON REBELXT 0x30ef Digital Rebel XT /* CATC products */ product CATC NETMATE 0x000a Netmate Ethernet product CATC NETMATE2 0x000c Netmate2 Ethernet product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer product CATC ANDROMEDA 0x1237 Andromeda hub /* CASIO products */ product CASIO QV_DIGICAM 0x1001 QV DigiCam product CASIO EXS880 0x1105 Exilim EX-S880 product CASIO BE300 0x2002 BE-300 PDA product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB /* CCYU products */ product CCYU ED1064 0x2136 EasyDisk ED1064 /* Century products */ product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure product CENTURY EX35SW4_SB4 0x011f Century USB Disk Enclosure /* Cherry products */ product CHERRY MY3000KBD 0x0001 My3000 keyboard product CHERRY MY3000HUB 0x0003 My3000 hub product CHERRY CYBOARD 0x0004 CyBoard Keyboard /* Chic Technology products */ product CHIC MOUSE1 0x0001 mouse product CHIC CYPRESS 0x0003 Cypress USB Mouse /* Chicony products */ product CHICONY KB8933 0x0001 KB-8933 keyboard product CHICONY KU0325 0x0116 KU-0325 keyboard product CHICONY CNF7129 0xb071 Notebook Web Camera product CHICONY HDUVCCAM 0xb40a HD UVC WebCam product CHICONY RTL8188CUS_1 0xaff7 RTL8188CUS product CHICONY RTL8188CUS_2 0xaff8 RTL8188CUS product CHICONY RTL8188CUS_3 0xaff9 RTL8188CUS product CHICONY RTL8188CUS_4 0xaffa RTL8188CUS product CHICONY RTL8188CUS_5 0xaffa RTL8188CUS product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera /* CH Products */ product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke /* Cisco-Linksys products */ product CISCOLINKSYS WUSB54AG 0x000c WUSB54AG Wireless Adapter product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter product CISCOLINKSYS USB200MV2 0x0018 USB200M v2 product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G product CISCOLINKSYS AE1000 0x002f AE1000 product CISCOLINKSYS USB3GIGV1 0x0041 USB3GIGV1 USB Ethernet Adapter product CISCOLINKSYS2 RT3070 0x4001 RT3070 product CISCOLINKSYS3 RT3070 0x0101 RT3070 /* Clipsal products */ product CLIPSAL 560884 0x0101 560884 C-Bus Audio Matrix Switch product CLIPSAL 5500PACA 0x0201 5500PACA C-Bus Pascal Automation Controller product CLIPSAL 5800PC 0x0301 5800PC C-Bus Wireless Interface product CLIPSAL 5500PCU 0x0303 5500PCU C-Bus Interface product CLIPSAL 5000CT2 0x0304 5000CT2 C-Bus Touch Screen product CLIPSAL C5000CT2 0x0305 C5000CT2 C-Bus Touch Screen product CLIPSAL L51xx 0x0401 L51xx C-Bus Dimmer /* CMOTECH products */ product CMOTECH CNU510 0x5141 CDMA Technologies USB modem product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem product CMOTECH CGU628 0x6006 CGU-628 product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem product CMOTECH DISK 0xf000 disk mode /* Compaq products */ product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC product COMPAQ PJB100 0x504a Personal Jukebox PJB100 product COMPAQ IPAQLINUX 0x505a iPAQ Linux /* Composite Corp products looks the same as "TANGTOP" */ product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor /* Conceptronic products */ product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN product CONCEPTRONIC C11U 0x7100 C11U product CONCEPTRONIC WL210 0x7110 WL-210 product CONCEPTRONIC AR5523_1 0x7801 AR5523 product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware) product CONCEPTRONIC AR5523_2 0x7811 AR5523 product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware) product CONCEPTRONIC2 RTL8192SU_1 0x3300 RTL8192SU product CONCEPTRONIC2 RTL8192SU_2 0x3301 RTL8192SU product CONCEPTRONIC2 RTL8192SU_3 0x3302 RTL8192SU product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN product CONCEPTRONIC2 C54RU2 0x3c22 C54RU product CONCEPTRONIC2 RT3070_1 0x3c08 RT3070 product CONCEPTRONIC2 RT3070_2 0x3c11 RT3070 product CONCEPTRONIC2 VIGORN61 0x3c25 VIGORN61 product CONCEPTRONIC2 RT2870_1 0x3c06 RT2870 product CONCEPTRONIC2 RT2870_2 0x3c07 RT2870 product CONCEPTRONIC2 RT2870_7 0x3c09 RT2870 product CONCEPTRONIC2 RT2870_8 0x3c12 RT2870 product CONCEPTRONIC2 RT2870_3 0x3c23 RT2870 product CONCEPTRONIC2 RT2870_4 0x3c25 RT2870 product CONCEPTRONIC2 RT2870_5 0x3c27 RT2870 product CONCEPTRONIC2 RT2870_6 0x3c28 RT2870 /* Connectix products */ product CONNECTIX QUICKCAM 0x0001 QuickCam /* Conect products */ product CONTEC COM1USBH 0x8311 FTDI compatible adapter /* Corega products */ product COREGA ETHER_USB_T 0x0001 Ether USB-T product COREGA FETHER_USB_TX 0x0004 FEther USB-TX product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11 product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS product COREGA WLANUSB 0x0012 Wireless LAN Stick-11 product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key product COREGA CGUSBRS232R 0x002a CG-USBRS232R product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX product COREGA RT2870_1 0x002f RT2870 product COREGA RT2870_2 0x003c RT2870 product COREGA RT2870_3 0x003f RT2870 product COREGA RT3070 0x0041 RT3070 product COREGA CGWLUSB300GNM 0x0042 CG-WLUSB300GNM product COREGA RTL8192SU 0x0047 RTL8192SU product COREGA RTL8192CU 0x0056 RTL8192CU product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11 product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC /* Corsair products */ product CORSAIR K60 0x0a60 Corsair Vengeance K60 keyboard product CORSAIR K70 0x1b09 Corsair Vengeance K70 keyboard /* Creative products */ product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG product CREATIVE NOMAD 0x4106 Nomad product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse /* Cambridge Silicon Radio Ltd. products */ product CSR BT_DONGLE 0x0001 Bluetooth USB dongle product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State /* Chipsbank Microelectronics Co., Ltd */ product CHIPSBANK USBMEMSTICK 0x6025 CBM2080 Flash drive controller product CHIPSBANK USBMEMSTICK1 0x6026 CBM1180 Flash drive controller /* CTX products */ product CTX EX1300 0x9999 Ex1300 hub /* Curitel products */ product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C) product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) product CURITEL PC5740 0x3701 Broadband Wireless modem product CURITEL UM150 0x3711 EVDO modem product CURITEL UM175 0x3714 EVDO modem /* CyberPower products */ product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD /* CyberTAN Technology products */ product CYBERTAN TG54USB 0x1666 TG54USB product CYBERTAN RT2870 0x1828 RT2870 /* Cypress Semiconductor products */ product CYPRESS MOUSE 0x0001 mouse product CYPRESS THERMO 0x0002 thermometer product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy product CYPRESS KBDHUB 0x0101 Keyboard/Hub product CYPRESS FMRADIO 0x1002 FM Radio product CYPRESS IKARILASER 0x121f Ikari Laser SteelSeries ApS product CYPRESS USBRS232 0x5500 USB-RS232 Interface product CYPRESS SLIM_HUB 0x6560 Slim Hub product CYPRESS XX6830XX 0x6830 PATA Storage Device product CYPRESS SILVERSHIELD 0xfd13 Gembird Silver Shield PM /* Daisy Technology products */ product DAISY DMC 0x6901 USB MultiMedia Reader /* Dallas Semiconductor products */ product DALLAS J6502 0x4201 J-6502 speakers /* DataApex products */ product DATAAPEX MULTICOM 0xead6 MultiCom /* Dell products */ product DELL PORT 0x0058 Port Replicator product DELL AIO926 0x5115 Photo AIO Printer 926 product DELL BC02 0x8000 BC02 Bluetooth USB Adapter product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN product DELL U5700 0x8114 Dell 5700 3G product DELL U5500 0x8115 Dell 5500 3G product DELL U5505 0x8116 Dell 5505 3G product DELL U5700_2 0x8117 Dell 5700 3G product DELL U5510 0x8118 Dell 5510 3G product DELL U5700_3 0x8128 Dell 5700 3G product DELL U5700_4 0x8129 Dell 5700 3G product DELL U5720 0x8133 Dell 5720 3G product DELL U5720_2 0x8134 Dell 5720 3G product DELL U740 0x8135 Dell U740 CDMA product DELL U5520 0x8136 Dell 5520 3G product DELL U5520_2 0x8137 Dell 5520 3G product DELL U5520_3 0x8138 Dell 5520 3G product DELL U5730 0x8180 Dell 5730 3G product DELL U5730_2 0x8181 Dell 5730 3G product DELL U5730_3 0x8182 Dell 5730 3G product DELL DW700 0x9500 Dell DW700 GPS /* Delorme Paublishing products */ product DELORME EARTHMATE 0x0100 Earthmate GPS /* Desknote products */ product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B /* Diamond products */ product DIAMOND RIO500USB 0x0001 Rio 500 USB /* Dick Smith Electronics (really C-Net) products */ product DICKSMITH RT2573 0x9022 RT2573 product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F /* Digi International products */ product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 /* Digianswer A/S products */ product DIGIANSWER ZIGBEE802154 0x000a ZigBee/802.15.4 MAC /* D-Link products */ /*product DLINK DSBS25 0x0100 DSB-S25 serial*/ product DLINK DUBE100 0x1a00 10/100 Ethernet product DLINK DUBE100C1 0x1a02 DUB-E100 rev C1 product DLINK DSB650TX4 0x200c 10/100 Ethernet product DLINK DWL120E 0x3200 DWL-120 rev E product DLINK DWA125D1 0x330f DWA-125 rev D1 product DLINK DWA123D1 0x3310 DWA-123 rev D1 product DLINK DWL122 0x3700 DWL-122 product DLINK DWLG120 0x3701 DWL-G120 product DLINK DWL120F 0x3702 DWL-120 rev F product DLINK DWLAG132 0x3a00 DWL-AG132 product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware) product DLINK DWLG132 0x3a02 DWL-G132 product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware) product DLINK DWLAG122 0x3a04 DWL-AG122 product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware) product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 product DLINK RT2870 0x3c09 RT2870 product DLINK RT3072 0x3c0a RT3072 product DLINK DWA140B3 0x3c15 DWA-140 rev B3 product DLINK DWA160B2 0x3c1a DWA-160 rev B2 product DLINK DWA127 0x3c1b DWA-127 Wireless Adapter product DLINK DWA162 0x3c1f DWA-162 Wireless Adapter product DLINK DWA140D1 0x3c20 DWA-140 rev D1 product DLINK DSB650C 0x4000 10Mbps Ethernet product DLINK DSB650TX1 0x4001 10/100 Ethernet product DLINK DSB650TX 0x4002 10/100 Ethernet product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet product DLINK DSB650TX3 0x400b 10/100 Ethernet product DLINK DSB650TX2 0x4102 10/100 Ethernet product DLINK DUB1312 0x4a00 10/100/1000 Ethernet product DLINK DSB650 0xabc1 10/100 Ethernet product DLINK DUBH7 0xf103 DUB-H7 USB 2.0 7-Port Hub product DLINK DWR510_CD 0xa805 DWR-510 CD-ROM Mode product DLINK DWR510 0x7e12 DWR-510 product DLINK DWM157 0x7d02 DWM-157 product DLINK DWM157_CD 0xa707 DWM-157 CD-ROM Mode product DLINK RTL8188CU 0x3308 RTL8188CU product DLINK RTL8192CU_1 0x3307 RTL8192CU product DLINK RTL8192CU_2 0x3309 RTL8192CU product DLINK RTL8192CU_3 0x330a RTL8192CU product DLINK DWA131B 0x330d DWA-131 rev B product DLINK2 RTL8192SU_1 0x3300 RTL8192SU product DLINK2 RTL8192SU_2 0x3302 RTL8192SU product DLINK2 DWA131A1 0x3303 DWA-131 A1 product DLINK2 DWA160A2 0x3a09 DWA-160 A2 product DLINK2 DWA120 0x3a0c DWA-120 product DLINK2 DWA120_NF 0x3a0d DWA-120 (no firmware) product DLINK2 DWA130D1 0x3a0f DWA-130 D1 product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1 product DLINK2 WUA1340 0x3c04 WUA-1340 product DLINK2 DWA111 0x3c06 DWA-111 product DLINK2 RT2870_1 0x3c09 RT2870 product DLINK2 DWA110 0x3c07 DWA-110 product DLINK2 RT3072 0x3c0a RT3072 product DLINK2 RT3072_1 0x3c0b RT3072 product DLINK2 RT3070_1 0x3c0d RT3070 product DLINK2 RT3070_2 0x3c0e RT3070 product DLINK2 RT3070_3 0x3c0f RT3070 product DLINK2 DWA160A1 0x3c10 DWA-160 A1 product DLINK2 RT2870_2 0x3c11 RT2870 product DLINK2 DWA130 0x3c13 DWA-130 product DLINK2 RT3070_4 0x3c15 RT3070 product DLINK2 RT3070_5 0x3c16 RT3070 product DLINK3 DWM652 0x3e04 DWM-652 /* DisplayLink products */ product DISPLAYLINK LCD4300U 0x01ba LCD-4300U product DISPLAYLINK LCD8000U 0x01bb LCD-8000U product DISPLAYLINK LD220 0x0100 Samsung LD220 product DISPLAYLINK GUC2020 0x0059 IOGEAR DVI GUC2020 product DISPLAYLINK VCUD60 0x0136 Rextron DVI product DISPLAYLINK CONV 0x0138 StarTech CONV-USB2DVI product DISPLAYLINK DLDVI 0x0141 DisplayLink DVI product DISPLAYLINK VGA10 0x015a CMP-USBVGA10 product DISPLAYLINK WSDVI 0x0198 WS Tech DVI product DISPLAYLINK EC008 0x019b EasyCAP008 DVI product DISPLAYLINK HPDOCK 0x01d4 HP USB Docking product DISPLAYLINK NL571 0x01d7 HP USB DVI product DISPLAYLINK M01061 0x01e2 Lenovo DVI product DISPLAYLINK SWDVI 0x024c SUNWEIT DVI product DISPLAYLINK NBDOCK 0x0215 VideoHome NBdock1920 product DISPLAYLINK LUM70 0x02a9 Lilliput UM-70 product DISPLAYLINK UM7X0 0x401a nanovision MiMo product DISPLAYLINK LT1421 0x03e0 Lenovo ThinkVision LT1421 product DISPLAYLINK POLARIS2 0x0117 Polaris2 USB dock product DISPLAYLINK PLUGABLE 0x0377 Plugable docking station /* DMI products */ product DMI CFSM_RW 0xa109 CF/SM Reader/Writer product DMI DISK 0x2bcf Generic Disk /* DrayTek products */ product DRAYTEK VIGOR550 0x0550 Vigor550 /* Dream Link products */ product DREAMLINK DL100B 0x0004 USB Webmail Notifier /* dresden elektronik products */ product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard product DRESDENELEKTRONIK WIRELESSHANDHELDTERMINAL 0x0004 Wireless Handheld Terminal product DRESDENELEKTRONIK DE_RFNODE 0x001c deRFnode product DRESDENELEKTRONIK LEVELSHIFTERSTICKLOWCOST 0x0022 Levelshifter Stick Low Cost /* DYMO */ product DYMO LABELMANAGERPNP 0x1001 DYMO LabelManager PnP /* Dynastream Innovations */ product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board product DYNASTREAM ANT2USB 0x1004 ANT2USB product DYNASTREAM ANTDEVBOARD2 0x1006 ANT dev board /* Edimax products */ product EDIMAX EW7318USG 0x7318 USB Wireless dongle product EDIMAX RTL8192SU_1 0x7611 RTL8192SU product EDIMAX RTL8192SU_2 0x7612 RTL8192SU product EDIMAX EW7622UMN 0x7622 EW-7622UMn product EDIMAX RT2870_1 0x7711 RT2870 product EDIMAX EW7717 0x7717 EW-7717 product EDIMAX EW7718 0x7718 EW-7718 product EDIMAX EW7733UND 0x7733 EW-7733UnD product EDIMAX EW7811UN 0x7811 EW-7811Un product EDIMAX RTL8192CU 0x7822 RTL8192CU /* eGalax Products */ product EGALAX TPANEL 0x0001 Touch Panel product EGALAX TPANEL2 0x0002 Touch Panel product EGALAX2 TPANEL 0x0001 Touch Panel /* EGO Products */ product EGO DUMMY 0x0000 Dummy Product product EGO M4U 0x1020 ESI M4U /* Eicon Networks */ product EICON DIVA852 0x4905 Diva 852 ISDN TA /* EIZO products */ product EIZO HUB 0x0000 hub product EIZO MONITOR 0x0001 monitor /* ELCON Systemtechnik products */ product ELCON PLAN 0x0002 Goldpfeil P-LAN /* Elecom products */ product ELECOM MOUSE29UO 0x0002 mouse 29UO product ELECOM LDUSBTX0 0x200c LD-USB/TX product ELECOM LDUSBTX1 0x4002 LD-USB/TX product ELECOM LDUSBLTX 0x4005 LD-USBL/TX product ELECOM WDC150SU2M 0x4008 WDC-150SU2M product ELECOM LDUSBTX2 0x400b LD-USB/TX product ELECOM LDUSB20 0x4010 LD-USB20 product ELECOM UCSGT 0x5003 UC-SGT product ELECOM UCSGT0 0x5004 UC-SGT product ELECOM LDUSBTX3 0xabc1 LD-USB/TX /* Elektor products */ product ELEKTOR FT323R 0x0005 FTDI compatible adapter /* Elsa products */ product ELSA MODEM1 0x2265 ELSA Modem Board product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet /* ELV products */ product ELV USBI2C 0xe00f USB-I2C interface /* EMS products */ product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter /* Emtec products */ product EMTEC RUF2PS 0x2240 Flash Drive /* Encore products */ product ENCORE RT3070_1 0x1480 RT3070 product ENCORE RT3070_2 0x14a1 RT3070 product ENCORE RT3070_3 0x14a9 RT3070 /* Entrega products */ product ENTREGA 1S 0x0001 1S serial product ENTREGA 2S 0x0002 2S serial product ENTREGA 1S25 0x0003 1S25 serial product ENTREGA 4S 0x0004 4S serial product ENTREGA E45 0x0005 E45 Ethernet product ENTREGA CENTRONICS 0x0006 Parallel Port product ENTREGA XX1 0x0008 Ethernet product ENTREGA 1S9 0x0093 1S9 serial product ENTREGA EZUSB 0x8000 EZ-USB /*product ENTREGA SERIAL 0x8001 DB25 Serial*/ product ENTREGA 2U4S 0x8004 2U4S serial/usb hub product ENTREGA XX2 0x8005 Ethernet /*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ /* Epson products */ product EPSON PRINTER1 0x0001 USB Printer product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac product EPSON PRINTER3 0x0003 ISD USB Smart Cable product EPSON PRINTER5 0x0005 USB Printer product EPSON 636 0x0101 Perfection 636U / 636Photo scanner product EPSON 610 0x0103 Perfection 610 scanner product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner product EPSON 1600 0x0107 Expression 1600 scanner product EPSON 1640 0x010a Perfection 1640SU scanner product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner product EPSON 640U 0x010c Perfection 640U scanner product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner product EPSON 1650 0x0110 Perfection 1650 scanner product EPSON GT9700F 0x0112 GT-9700F scanner product EPSON GT9300UF 0x011b GT-9300UF scanner product EPSON 3200 0x011c Perfection 3200 scanner product EPSON 1260 0x011d Perfection 1260 scanner product EPSON 1660 0x011e Perfection 1660 scanner product EPSON 1670 0x011f Perfection 1670 scanner product EPSON 1270 0x0120 Perfection 1270 scanner product EPSON 2480 0x0121 Perfection 2480 scanner product EPSON 3590 0x0122 Perfection 3590 scanner product EPSON 4990 0x012a Perfection 4990 Photo scanner product EPSON CRESSI_EDY 0x0521 Cressi Edy diving computer product EPSON N2ITION3 0x0522 Zeagle N2iTion3 diving computer product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader product EPSON CX5400 0x0808 CX5400 scanner product EPSON 3500 0x080e CX-3500/3600/3650 MFP product EPSON RX425 0x080f Stylus Photo RX425 scanner product EPSON DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner product EPSON DX4000 0x082f DX4000 MFP scanner product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner product EPSON SX100 0x0841 SX100/NX100 MFP scanner product EPSON NX300 0x0848 NX300 MFP scanner product EPSON SX200 0x0849 SX200/SX205 MFP scanner product EPSON SX400 0x084a SX400/NX400/TX400 MFP scanner /* e-TEK Labs products */ product ETEK 1COM 0x8007 Serial /* Evolution products */ product EVOLUTION ER1 0x0300 FTDI compatible adapter product EVOLUTION HYBRID 0x0302 FTDI compatible adapter product EVOLUTION RCM4 0x0303 FTDI compatible adapter /* Extended Systems products */ product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA /* Falcom products */ product FALCOM TWIST 0x0001 USB GSM/GPRS Modem product FALCOM SAMBA 0x0005 FTDI compatible adapter /* FEIYA products */ product FEIYA DUMMY 0x0000 Dummy product product FEIYA 5IN1 0x1132 5-in-1 Card Reader product FEIYA ELANGO 0x6200 MicroSDHC Card Reader product FEIYA AC110 0x6300 AC-110 Card Reader /* FeiXun Communication products */ product FEIXUN RTL8188CU 0x0090 RTL8188CU product FEIXUN RTL8192CU 0x0091 RTL8192CU /* Festo */ product FESTO CPX_USB 0x0102 CPX-USB product FESTO CMSP 0x0501 CMSP /* Fiberline */ product FIBERLINE WL430U 0x6003 WL-430U /* FIC / OpenMoko */ product FIC NEO1973_DEBUG 0x5118 FTDI compatible adapter /* Fossil, Inc products */ product FOSSIL WRISTPDA 0x0002 Wrist PDA /* Foxconn products */ product FOXCONN TCOM_TC_300 0xe000 T-Com TC 300 product FOXCONN PIRELLI_DP_L10 0xe003 Pirelli DP-L10 /* Freecom products */ product FREECOM DVD 0xfc01 DVD drive product FREECOM HDD 0xfc05 Classic SL Hard Drive /* Fujitsu Siemens Computers products */ product FSC E5400 0x1009 PrismGT USB 2.0 WLAN /* Future Technology Devices products */ product FTDI SCX8_USB_PHOENIX 0x5259 SCx8 USB Phoenix interface product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial product FTDI SERIAL_8U232AM4 0x6004 8U232AM Serial product FTDI SERIAL_232RL 0x6006 FT232RL Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial product FTDI 232H 0x6014 FTDI compatible adapter product FTDI 232EX 0x6015 FTDI compatible adapter product FTDI SERIAL_2232D 0x9e90 FT2232D Dual port Serial product FTDI SERIAL_4232H 0x6011 FT4232H Quad port Serial product FTDI XDS100V2 0xa6d0 TI XDS100V1/V2 and early Beaglebones product FTDI XDS100V3 0xa6d1 TI XDS100V3 product FTDI KTLINK 0xbbe2 KT-LINK Embedded Hackers Multitool product FTDI TURTELIZER2 0xbdc8 egnite Turtelizer 2 JTAG/RS232 Adapter /* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal product FTDI GAMMASCOUT 0xd678 Gamma-Scout product FTDI KBS 0xe6c8 Pyramid KBS USB LCD product FTDI EISCOU 0xe888 Expert ISDN Control USB product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II product FTDI PCMSFU 0xe88b Precision Clock MSF USB product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG product FTDI MAXSTREAM 0xee18 Maxstream PKG-U product FTDI USB_UIRT 0xf850 USB-UIRT product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation /* Commerzielle und Technische Informationssysteme GmbH products */ product FTDI CTI_USB_NANO_485 0xf60b CTI USB-Nano 485 product FTDI CTI_USB_MINI_485 0xf608 CTI USB-Mini 485 /* Other products */ product FTDI 232RL 0xfbfa FTDI compatible adapter product FTDI 4N_GALAXY_DE_1 0xf3c0 FTDI compatible adapter product FTDI 4N_GALAXY_DE_2 0xf3c1 FTDI compatible adapter product FTDI 4N_GALAXY_DE_3 0xf3c2 FTDI compatible adapter product FTDI 8U232AM_ALT 0x6006 FTDI compatible adapter product FTDI ACCESSO 0xfad0 FTDI compatible adapter product FTDI ACG_HFDUAL 0xdd20 FTDI compatible adapter product FTDI ACTIVE_ROBOTS 0xe548 FTDI compatible adapter product FTDI ACTZWAVE 0xf2d0 FTDI compatible adapter product FTDI AMC232 0xff00 FTDI compatible adapter product FTDI ARTEMIS 0xdf28 FTDI compatible adapter product FTDI ASK_RDR400 0xc991 FTDI compatible adapter product FTDI ATIK_ATK16 0xdf30 FTDI compatible adapter product FTDI ATIK_ATK16C 0xdf32 FTDI compatible adapter product FTDI ATIK_ATK16HR 0xdf31 FTDI compatible adapter product FTDI ATIK_ATK16HRC 0xdf33 FTDI compatible adapter product FTDI ATIK_ATK16IC 0xdf35 FTDI compatible adapter product FTDI BCS_SE923 0xfb99 FTDI compatible adapter product FTDI CANDAPTER 0x9f80 FTDI compatible adapter product FTDI CANUSB 0xffa8 FTDI compatible adapter product FTDI CCSICDU20_0 0xf9d0 FTDI compatible adapter product FTDI CCSICDU40_1 0xf9d1 FTDI compatible adapter product FTDI CCSICDU64_4 0xf9d4 FTDI compatible adapter product FTDI CCSLOAD_N_GO_3 0xf9d3 FTDI compatible adapter product FTDI CCSMACHX_2 0xf9d2 FTDI compatible adapter product FTDI CCSPRIME8_5 0xf9d5 FTDI compatible adapter product FTDI CHAMSYS_24_MASTER_WING 0xdaf8 FTDI compatible adapter product FTDI CHAMSYS_MAXI_WING 0xdafd FTDI compatible adapter product FTDI CHAMSYS_MEDIA_WING 0xdafe FTDI compatible adapter product FTDI CHAMSYS_MIDI_TIMECODE 0xdafb FTDI compatible adapter product FTDI CHAMSYS_MINI_WING 0xdafc FTDI compatible adapter product FTDI CHAMSYS_PC_WING 0xdaf9 FTDI compatible adapter product FTDI CHAMSYS_USB_DMX 0xdafa FTDI compatible adapter product FTDI CHAMSYS_WING 0xdaff FTDI compatible adapter product FTDI COM4SM 0xd578 FTDI compatible adapter product FTDI CONVERTER_0 0xd388 FTDI compatible adapter product FTDI CONVERTER_1 0xd389 FTDI compatible adapter product FTDI CONVERTER_2 0xd38a FTDI compatible adapter product FTDI CONVERTER_3 0xd38b FTDI compatible adapter product FTDI CONVERTER_4 0xd38c FTDI compatible adapter product FTDI CONVERTER_5 0xd38d FTDI compatible adapter product FTDI CONVERTER_6 0xd38e FTDI compatible adapter product FTDI CONVERTER_7 0xd38f FTDI compatible adapter product FTDI DMX4ALL 0xc850 FTDI compatible adapter product FTDI DOMINTELL_DGQG 0xef50 FTDI compatible adapter product FTDI DOMINTELL_DUSB 0xef51 FTDI compatible adapter product FTDI DOTEC 0x9868 FTDI compatible adapter product FTDI ECLO_COM_1WIRE 0xea90 FTDI compatible adapter product FTDI ECO_PRO_CDS 0xe520 FTDI compatible adapter product FTDI ELSTER_UNICOM 0xe700 FTDI compatible adapter product FTDI ELV_ALC8500 0xf06e FTDI compatible adapter product FTDI ELV_CLI7000 0xfb59 FTDI compatible adapter product FTDI ELV_CSI8 0xe0f0 FTDI compatible adapter product FTDI ELV_EC3000 0xe006 FTDI compatible adapter product FTDI ELV_EM1000DL 0xe0f1 FTDI compatible adapter product FTDI ELV_EM1010PC 0xe0ef FTDI compatible adapter product FTDI ELV_FEM 0xe00a FTDI compatible adapter product FTDI ELV_FHZ1000PC 0xf06f FTDI compatible adapter product FTDI ELV_FHZ1300PC 0xe0e8 FTDI compatible adapter product FTDI ELV_FM3RX 0xe0ed FTDI compatible adapter product FTDI ELV_FS20SIG 0xe0f4 FTDI compatible adapter product FTDI ELV_HS485 0xe0ea FTDI compatible adapter product FTDI ELV_KL100 0xe002 FTDI compatible adapter product FTDI ELV_MSM1 0xe001 FTDI compatible adapter product FTDI ELV_PCD200 0xf06c FTDI compatible adapter product FTDI ELV_PCK100 0xe0f2 FTDI compatible adapter product FTDI ELV_PPS7330 0xfb5c FTDI compatible adapter product FTDI ELV_RFP500 0xe0f3 FTDI compatible adapter product FTDI ELV_T1100 0xf06b FTDI compatible adapter product FTDI ELV_TFD128 0xe0ec FTDI compatible adapter product FTDI ELV_TFM100 0xfb5d FTDI compatible adapter product FTDI ELV_TWS550 0xe009 FTDI compatible adapter product FTDI ELV_UAD8 0xf068 FTDI compatible adapter product FTDI ELV_UDA7 0xf069 FTDI compatible adapter product FTDI ELV_UDF77 0xfb5e FTDI compatible adapter product FTDI ELV_UIO88 0xfb5f FTDI compatible adapter product FTDI ELV_ULA200 0xf06d FTDI compatible adapter product FTDI ELV_UM100 0xfb5a FTDI compatible adapter product FTDI ELV_UMS100 0xe0eb FTDI compatible adapter product FTDI ELV_UO100 0xfb5b FTDI compatible adapter product FTDI ELV_UR100 0xfb58 FTDI compatible adapter product FTDI ELV_USI2 0xf06a FTDI compatible adapter product FTDI ELV_USR 0xe000 FTDI compatible adapter product FTDI ELV_UTP8 0xe0f5 FTDI compatible adapter product FTDI ELV_WS300PC 0xe0f6 FTDI compatible adapter product FTDI ELV_WS444PC 0xe0f7 FTDI compatible adapter product FTDI ELV_WS500 0xe0e9 FTDI compatible adapter product FTDI ELV_WS550 0xe004 FTDI compatible adapter product FTDI ELV_WS777 0xe0ee FTDI compatible adapter product FTDI ELV_WS888 0xe008 FTDI compatible adapter product FTDI FUTURE_0 0xf44a FTDI compatible adapter product FTDI FUTURE_1 0xf44b FTDI compatible adapter product FTDI FUTURE_2 0xf44c FTDI compatible adapter product FTDI GENERIC 0x9378 FTDI compatible adapter product FTDI GUDEADS_E808 0xe808 FTDI compatible adapter product FTDI GUDEADS_E809 0xe809 FTDI compatible adapter product FTDI GUDEADS_E80A 0xe80a FTDI compatible adapter product FTDI GUDEADS_E80B 0xe80b FTDI compatible adapter product FTDI GUDEADS_E80C 0xe80c FTDI compatible adapter product FTDI GUDEADS_E80D 0xe80d FTDI compatible adapter product FTDI GUDEADS_E80E 0xe80e FTDI compatible adapter product FTDI GUDEADS_E80F 0xe80f FTDI compatible adapter product FTDI GUDEADS_E88D 0xe88d FTDI compatible adapter product FTDI GUDEADS_E88E 0xe88e FTDI compatible adapter product FTDI GUDEADS_E88F 0xe88f FTDI compatible adapter product FTDI HD_RADIO 0x937c FTDI compatible adapter product FTDI HO720 0xed72 FTDI compatible adapter product FTDI HO730 0xed73 FTDI compatible adapter product FTDI HO820 0xed74 FTDI compatible adapter product FTDI HO870 0xed71 FTDI compatible adapter product FTDI IBS_APP70 0xff3d FTDI compatible adapter product FTDI IBS_PCMCIA 0xff3a FTDI compatible adapter product FTDI IBS_PEDO 0xff3e FTDI compatible adapter product FTDI IBS_PICPRO 0xff39 FTDI compatible adapter product FTDI IBS_PK1 0xff3b FTDI compatible adapter product FTDI IBS_PROD 0xff3f FTDI compatible adapter product FTDI IBS_RS232MON 0xff3c FTDI compatible adapter product FTDI IBS_US485 0xff38 FTDI compatible adapter product FTDI IPLUS 0xd070 FTDI compatible adapter product FTDI IPLUS2 0xd071 FTDI compatible adapter product FTDI IRTRANS 0xfc60 FTDI compatible adapter product FTDI LENZ_LIUSB 0xd780 FTDI compatible adapter product FTDI LM3S_DEVEL_BOARD 0xbcd8 FTDI compatible adapter product FTDI LM3S_EVAL_BOARD 0xbcd9 FTDI compatible adapter product FTDI LM3S_ICDI_B_BOARD 0xbcda FTDI compatible adapter product FTDI MASTERDEVEL2 0xf449 FTDI compatible adapter product FTDI MHAM_DB9 0xeeed FTDI compatible adapter product FTDI MHAM_IC 0xeeec FTDI compatible adapter product FTDI MHAM_KW 0xeee8 FTDI compatible adapter product FTDI MHAM_RS232 0xeeee FTDI compatible adapter product FTDI MHAM_Y6 0xeeea FTDI compatible adapter product FTDI MHAM_Y8 0xeeeb FTDI compatible adapter product FTDI MHAM_Y9 0xeeef FTDI compatible adapter product FTDI MHAM_YS 0xeee9 FTDI compatible adapter product FTDI MICRO_CHAMELEON 0xcaa0 FTDI compatible adapter product FTDI MTXORB_5 0xfa05 FTDI compatible adapter product FTDI MTXORB_6 0xfa06 FTDI compatible adapter product FTDI NXTCAM 0xabb8 FTDI compatible adapter product FTDI OCEANIC 0xf460 FTDI compatible adapter product FTDI OOCDLINK 0xbaf8 FTDI compatible adapter product FTDI OPENDCC 0xbfd8 FTDI compatible adapter product FTDI OPENDCC_GATEWAY 0xbfdb FTDI compatible adapter product FTDI OPENDCC_GBM 0xbfdc FTDI compatible adapter product FTDI OPENDCC_SNIFFER 0xbfd9 FTDI compatible adapter product FTDI OPENDCC_THROTTLE 0xbfda FTDI compatible adapter product FTDI PCDJ_DAC2 0xfa88 FTDI compatible adapter product FTDI PERLE_ULTRAPORT 0xf0c0 FTDI compatible adapter product FTDI PHI_FISCO 0xe40b FTDI compatible adapter product FTDI PIEGROUP 0xf208 FTDI compatible adapter product FTDI PROPOX_JTAGCABLEII 0xd738 FTDI compatible adapter product FTDI R2000KU_TRUE_RNG 0xfb80 FTDI compatible adapter product FTDI R2X0 0xfc71 FTDI compatible adapter product FTDI RELAIS 0xfa10 FTDI compatible adapter product FTDI REU_TINY 0xed22 FTDI compatible adapter product FTDI RMP200 0xe729 FTDI compatible adapter product FTDI RM_CANVIEW 0xfd60 FTDI compatible adapter product FTDI RRCIRKITS_LOCOBUFFER 0xc7d0 FTDI compatible adapter product FTDI SCIENCESCOPE_HS_LOGBOOK 0xff1d FTDI compatible adapter product FTDI SCIENCESCOPE_LOGBOOKML 0xff18 FTDI compatible adapter product FTDI SCIENCESCOPE_LS_LOGBOOK 0xff1c FTDI compatible adapter product FTDI SCS_DEVICE_0 0xd010 FTDI compatible adapter product FTDI SCS_DEVICE_1 0xd011 FTDI compatible adapter product FTDI SCS_DEVICE_2 0xd012 FTDI compatible adapter product FTDI SCS_DEVICE_3 0xd013 FTDI compatible adapter product FTDI SCS_DEVICE_4 0xd014 FTDI compatible adapter product FTDI SCS_DEVICE_5 0xd015 FTDI compatible adapter product FTDI SCS_DEVICE_6 0xd016 FTDI compatible adapter product FTDI SCS_DEVICE_7 0xd017 FTDI compatible adapter product FTDI SDMUSBQSS 0xf448 FTDI compatible adapter product FTDI SIGNALYZER_SH2 0xbca2 FTDI compatible adapter product FTDI SIGNALYZER_SH4 0xbca4 FTDI compatible adapter product FTDI SIGNALYZER_SLITE 0xbca1 FTDI compatible adapter product FTDI SIGNALYZER_ST 0xbca0 FTDI compatible adapter product FTDI SPECIAL_1 0xfc70 FTDI compatible adapter product FTDI SPECIAL_3 0xfc72 FTDI compatible adapter product FTDI SPECIAL_4 0xfc73 FTDI compatible adapter product FTDI SPROG_II 0xf0c8 FTDI compatible adapter product FTDI SR_RADIO 0x9379 FTDI compatible adapter product FTDI SUUNTO_SPORTS 0xf680 FTDI compatible adapter product FTDI TAVIR_STK500 0xfa33 FTDI compatible adapter product FTDI TERATRONIK_D2XX 0xec89 FTDI compatible adapter product FTDI TERATRONIK_VCP 0xec88 FTDI compatible adapter product FTDI THORLABS 0xfaf0 FTDI compatible adapter product FTDI TNC_X 0xebe0 FTDI compatible adapter product FTDI TTUSB 0xff20 FTDI compatible adapter product FTDI USBX_707 0xf857 FTDI compatible adapter product FTDI USINT_CAT 0xb810 FTDI compatible adapter product FTDI USINT_RS232 0xb812 FTDI compatible adapter product FTDI USINT_WKEY 0xb811 FTDI compatible adapter product FTDI VARDAAN 0xf070 FTDI compatible adapter product FTDI VNHCPCUSB_D 0xfe38 FTDI compatible adapter product FTDI WESTREX_MODEL_777 0xdc00 FTDI compatible adapter product FTDI WESTREX_MODEL_8900F 0xdc01 FTDI compatible adapter product FTDI XF_547 0xfc0a FTDI compatible adapter product FTDI XF_640 0xfc0e FTDI compatible adapter product FTDI XF_642 0xfc0f FTDI compatible adapter product FTDI XM_RADIO 0x937a FTDI compatible adapter product FTDI YEI_SERVOCENTER31 0xe050 FTDI compatible adapter /* Fuji photo products */ product FUJIPHOTO MASS0100 0x0100 Mass Storage /* Fujitsu protducts */ product FUJITSU AH_F401U 0x105b AH-F401U Air H device /* Fujitsu-Siemens protducts */ product FUJITSUSIEMENS SCR 0x0009 Fujitsu-Siemens SCR USB Reader /* Garmin products */ product GARMIN IQUE_3600 0x0004 iQue 3600 /* Gemalto products */ product GEMALTO PROXPU 0x5501 Prox-PU/CU RFID Card Reader /* General Instruments (Motorola) products */ product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem /* Genesys Logic products */ product GENESYS GL620USB 0x0501 GL620USB Host-Host interface product GENESYS GL650 0x0604 GL650 HUB product GENESYS GL606 0x0606 USB 2.0 HUB product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader /* GIGABYTE products */ product GIGABYTE GN54G 0x8001 GN-54G product GIGABYTE GNBR402W 0x8002 GN-BR402W product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 product GIGABYTE GNWBKG 0x8007 GN-WBKG product GIGABYTE GNWB01GS 0x8008 GN-WB01GS product GIGABYTE GNWI05GS 0x800a GN-WI05GS /* Gigaset products */ product GIGASET WLAN 0x0701 WLAN product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware) product GIGASET AR5523 0x0712 AR5523 product GIGASET AR5523_NF 0x0713 AR5523 (no firmware) product GIGASET RT2573 0x0722 RT2573 product GIGASET RT3070_1 0x0740 RT3070 product GIGASET RT3070_2 0x0744 RT3070 product GIGABYTE RT2870_1 0x800b RT2870 product GIGABYTE GNWB31N 0x800c GN-WB31N product GIGABYTE GNWB32L 0x800d GN-WB32L /* Global Sun Technology product */ product GLOBALSUN AR5523_1 0x7801 AR5523 product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware) product GLOBALSUN AR5523_2 0x7811 AR5523 product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware) /* Globespan products */ product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* G.Mate, Inc products */ product GMATE YP3X00 0x1001 YP3X00 PDA /* GN Otometrics */ product GNOTOMETRICS USB 0x0010 FTDI compatible adapter /* GoHubs products */ product GOHUBS GOCOM232 0x1001 GoCOM232 Serial /* Good Way Technology products */ product GOODWAY GWUSB2E 0x6200 GWUSB2E product GOODWAY RT2573 0xc019 RT2573 /* Google products */ product GOOGLE NEXUSONE 0x4e11 Nexus One /* Gravis products */ product GRAVIS GAMEPADPRO 0x4001 GamePad Pro /* GREENHOUSE products */ product GREENHOUSE KANA21 0x0001 CF-writer with MP3 /* Griffin Technology */ product GRIFFIN IMATE 0x0405 iMate, ADB Adapter /* Guillemot Corporation */ product GUILLEMOT DALEADER 0xa300 DA Leader product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP product GUILLEMOT HWNU300 0xe030 HWNU-300 product GUILLEMOT HWNUM300 0xe031 HWNUm-300 product GUILLEMOT HWGUN54 0xe032 HWGUn-54 product GUILLEMOT HWNUP150 0xe033 HWNUP-150 /* Hagiwara products */ product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader product HAGIWARA FG 0x0005 FlashGate /* HAL Corporation products */ product HAL IMR001 0x0011 Crossam2+USB IR commander /* Handspring, Inc. */ product HANDSPRING VISOR 0x0100 Handspring Visor product HANDSPRING TREO 0x0200 Handspring Treo product HANDSPRING TREO600 0x0300 Handspring Treo 600 /* Hauppauge Computer Works */ product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM product HAUPPAUGE2 NOVAT500 0x9580 NovaT 500Stick /* Hawking Technologies products */ product HAWKING RT2870_1 0x0001 RT2870 product HAWKING RT2870_2 0x0003 RT2870 product HAWKING HWUN2 0x0009 HWUN2 product HAWKING RT3070 0x000b RT3070 product HAWKING RTL8192CU 0x0019 RTL8192CU product HAWKING UF100 0x400c 10/100 USB Ethernet product HAWKING RTL8192SU_1 0x0015 RTL8192SU product HAWKING RTL8192SU_2 0x0016 RTL8192SU /* HID Global GmbH products */ product HIDGLOBAL CM2020 0x0596 Omnikey Cardman 2020 product HIDGLOBAL CM6020 0x1784 Omnikey Cardman 6020 /* Hitachi, Ltd. products */ product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface /* HP products */ product HP 895C 0x0004 DeskJet 895C product HP 4100C 0x0101 Scanjet 4100C product HP S20 0x0102 Photosmart S20 product HP 880C 0x0104 DeskJet 880C product HP 4200C 0x0105 ScanJet 4200C product HP CDWRITERPLUS 0x0107 CD-Writer Plus product HP KBDHUB 0x010c Multimedia Keyboard Hub product HP G55XI 0x0111 OfficeJet G55xi product HP HN210W 0x011c HN210W 802.11b WLAN product HP 49GPLUS 0x0121 49g+ graphing calculator product HP 6200C 0x0201 ScanJet 6200C product HP S20b 0x0202 PhotoSmart S20 product HP 815C 0x0204 DeskJet 815C product HP 3300C 0x0205 ScanJet 3300C product HP CDW8200 0x0207 CD-Writer Plus 8200e product HP MMKEYB 0x020c Multimedia keyboard product HP 1220C 0x0212 DeskJet 1220C product HP UN2420_QDL 0x241d UN2420 QDL Firmware Loader product HP UN2420 0x251d UN2420 WWAN/GPS Module product HP 810C 0x0304 DeskJet 810C/812C product HP 4300C 0x0305 Scanjet 4300C product HP CDW4E 0x0307 CD-Writer+ CD-4e product HP G85XI 0x0311 OfficeJet G85xi product HP 1200 0x0317 LaserJet 1200 product HP 5200C 0x0401 Scanjet 5200C product HP 830C 0x0404 DeskJet 830C product HP 3400CSE 0x0405 ScanJet 3400cse product HP 6300C 0x0601 Scanjet 6300C product HP 840C 0x0604 DeskJet 840c product HP 2200C 0x0605 ScanJet 2200C product HP 5300C 0x0701 Scanjet 5300C product HP 4400C 0x0705 Scanjet 4400C product HP 4470C 0x0805 Scanjet 4470C product HP 82x0C 0x0b01 Scanjet 82x0C product HP 2300D 0x0b17 Laserjet 2300d product HP 970CSE 0x1004 Deskjet 970Cse product HP 5400C 0x1005 Scanjet 5400C product HP 2215 0x1016 iPAQ 22xx/Jornada 548 product HP 568J 0x1116 Jornada 568 product HP 930C 0x1204 DeskJet 930c product HP3 RTL8188CU 0x1629 RTL8188CU product HP P2000U 0x1801 Inkjet P-2000U product HP HS2300 0x1e1d HS2300 HSDPA (aka MC8775) product HP 640C 0x2004 DeskJet 640c product HP 4670V 0x3005 ScanJet 4670v product HP P1100 0x3102 Photosmart P1100 product HP LD220 0x3524 LD220 POS Display product HP OJ4215 0x3d11 OfficeJet 4215 product HP HN210E 0x811c Ethernet HN210E product HP2 C500 0x6002 PhotoSmart C500 product HP EV2200 0x1b1d ev2200 HSDPA (aka MC5720) product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775) /* HTC products */ product HTC WINMOBILE 0x00ce HTC USB Sync product HTC PPC6700MODEM 0x00cf PPC6700 Modem product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync product HTC WIZARD 0x0bce HTC Wizard USB Sync product HTC LEGENDSYNC 0x0c97 HTC Legend USB Sync product HTC LEGEND 0x0ff9 HTC Legend product HTC LEGENDINTERNET 0x0ffe HTC Legend Internet Sharing /* HUAWEI products */ product HUAWEI MOBILE 0x1001 Huawei Mobile product HUAWEI E220 0x1003 HSDPA modem product HUAWEI E220BIS 0x1004 HSDPA modem product HUAWEI E1401 0x1401 3G modem product HUAWEI E1402 0x1402 3G modem product HUAWEI E1403 0x1403 3G modem product HUAWEI E1404 0x1404 3G modem product HUAWEI E1405 0x1405 3G modem product HUAWEI E1406 0x1406 3G modem product HUAWEI E1407 0x1407 3G modem product HUAWEI E1408 0x1408 3G modem product HUAWEI E1409 0x1409 3G modem product HUAWEI E140A 0x140a 3G modem product HUAWEI E140B 0x140b 3G modem product HUAWEI E180V 0x140c E180V product HUAWEI E140D 0x140d 3G modem product HUAWEI E140E 0x140e 3G modem product HUAWEI E140F 0x140f 3G modem product HUAWEI E1410 0x1410 3G modem product HUAWEI E1411 0x1411 3G modem product HUAWEI E1412 0x1412 3G modem product HUAWEI E1413 0x1413 3G modem product HUAWEI E1414 0x1414 3G modem product HUAWEI E1415 0x1415 3G modem product HUAWEI E1416 0x1416 3G modem product HUAWEI E1417 0x1417 3G modem product HUAWEI E1418 0x1418 3G modem product HUAWEI E1419 0x1419 3G modem product HUAWEI E141A 0x141a 3G modem product HUAWEI E141B 0x141b 3G modem product HUAWEI E141C 0x141c 3G modem product HUAWEI E141D 0x141d 3G modem product HUAWEI E141E 0x141e 3G modem product HUAWEI E141F 0x141f 3G modem product HUAWEI E1420 0x1420 3G modem product HUAWEI E1421 0x1421 3G modem product HUAWEI E1422 0x1422 3G modem product HUAWEI E1423 0x1423 3G modem product HUAWEI E1424 0x1424 3G modem product HUAWEI E1425 0x1425 3G modem product HUAWEI E1426 0x1426 3G modem product HUAWEI E1427 0x1427 3G modem product HUAWEI E1428 0x1428 3G modem product HUAWEI E1429 0x1429 3G modem product HUAWEI E142A 0x142a 3G modem product HUAWEI E142B 0x142b 3G modem product HUAWEI E142C 0x142c 3G modem product HUAWEI E142D 0x142d 3G modem product HUAWEI E142E 0x142e 3G modem product HUAWEI E142F 0x142f 3G modem product HUAWEI E1430 0x1430 3G modem product HUAWEI E1431 0x1431 3G modem product HUAWEI E1432 0x1432 3G modem product HUAWEI E1433 0x1433 3G modem product HUAWEI E1434 0x1434 3G modem product HUAWEI E1435 0x1435 3G modem product HUAWEI E1436 0x1436 3G modem product HUAWEI E1437 0x1437 3G modem product HUAWEI E1438 0x1438 3G modem product HUAWEI E1439 0x1439 3G modem product HUAWEI E143A 0x143a 3G modem product HUAWEI E143B 0x143b 3G modem product HUAWEI E143C 0x143c 3G modem product HUAWEI E143D 0x143d 3G modem product HUAWEI E143E 0x143e 3G modem product HUAWEI E143F 0x143f 3G modem product HUAWEI E1752 0x1446 3G modem product HUAWEI K4505 0x1464 3G modem product HUAWEI K3765 0x1465 3G modem product HUAWEI E1820 0x14ac E1820 HSPA+ USB Slider product HUAWEI K3770 0x14c9 3G modem product HUAWEI K3772 0x14cf K3772 product HUAWEI K3770_INIT 0x14d1 K3770 Initial product HUAWEI E3131_INIT 0x14fe 3G modem initial product HUAWEI E392 0x1505 LTE modem product HUAWEI E3131 0x1506 3G modem product HUAWEI K3765_INIT 0x1520 K3765 Initial product HUAWEI K4505_INIT 0x1521 K4505 Initial product HUAWEI K3772_INIT 0x1526 K3772 Initial product HUAWEI E3272_INIT 0x155b LTE modem initial product HUAWEI ME909U 0x1573 LTE modem product HUAWEI R215_INIT 0x1582 LTE modem initial product HUAWEI R215 0x1588 LTE modem product HUAWEI ETS2055 0x1803 CDMA modem product HUAWEI E173 0x1c05 3G modem product HUAWEI E173_INIT 0x1c0b 3G modem initial product HUAWEI E3272 0x1c1e LTE modem /* HUAWEI 3com products */ product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g /* IBM Corporation */ product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive /* Icom products */ product ICOM SP1 0x0004 FTDI compatible adapter product ICOM OPC_U_UC 0x0018 FTDI compatible adapter product ICOM RP2C1 0x0009 FTDI compatible adapter product ICOM RP2C2 0x000a FTDI compatible adapter product ICOM RP2D 0x000b FTDI compatible adapter product ICOM RP2KVR 0x0013 FTDI compatible adapter product ICOM RP2KVT 0x0012 FTDI compatible adapter product ICOM RP2VR 0x000d FTDI compatible adapter product ICOM RP2VT 0x000c FTDI compatible adapter product ICOM RP4KVR 0x0011 FTDI compatible adapter product ICOM RP4KVT 0x0010 FTDI compatible adapter /* ID-tech products */ product IDTECH IDT1221U 0x0300 FTDI compatible adapter /* Imagination Technologies products */ product IMAGINATION DBX1 0x2107 DBX1 DSP core /* Initio Corporation products */ product INITIO DUMMY 0x0000 Dummy product product INITIO INIC_1610P 0x1e40 USB to SATA Bridge /* Inside Out Networks products */ product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports /* In-System products */ product INSYSTEM F5U002 0x0002 Parallel printer product INSYSTEM ATAPI 0x0031 ATAPI Adapter product INSYSTEM ISD110 0x0200 IDE Adapter ISD110 product INSYSTEM ISD105 0x0202 IDE Adapter ISD105 product INSYSTEM USBCABLE 0x081a USB cable product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2 /* Intel products */ product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera product INTEL TESTBOARD 0x9890 82930 test board product INTEL2 IRMH 0x0020 Integrated Rate Matching Hub product INTEL2 IRMH2 0x0024 Integrated Rate Matching Hub product INTEL2 IRMH3 0x8000 Integrated Rate Matching Hub product INTEL2 IRMH4 0x8008 Integrated Rate Matching Hub /* Interbiometric products */ product INTERBIOMETRICS IOBOARD 0x1002 FTDI compatible adapter product INTERBIOMETRICS MINI_IOBOARD 0x1006 FTDI compatible adapter /* Intersil products */ product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN /* Interpid Control Systems products */ product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface /* I/O DATA products */ product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card product IODATA USBSDRW 0x031e USB-SDRW SD-card product IODATA USBETT 0x0901 USB ETT product IODATA USBETTX 0x0904 USB ETTX product IODATA USBETTXS 0x0913 USB ETTX product IODATA USBWNB11A 0x0919 USB WN-B11 product IODATA USBWNB11 0x0922 USB Airport WN-B11 product IODATA ETGUS2 0x0930 ETG-US2 product IODATA WNGDNUS2 0x093f WN-GDN/US2 product IODATA RT3072_1 0x0944 RT3072 product IODATA RT3072_2 0x0945 RT3072 product IODATA RT3072_3 0x0947 RT3072 product IODATA RT3072_4 0x0948 RT3072 product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 product IODATA USBRSAQ5 0x0a0e Serial USB-RSAQ5 product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC /* Iomega products */ product IOMEGA ZIP100 0x0001 Zip 100 product IOMEGA ZIP250 0x0030 Zip 250 /* Ionic products */ product IONICS PLUGCOMPUTER 0x0102 FTDI compatible adapter /* Integrated System Solution Corp. products */ product ISSC ISSCBTA 0x1001 Bluetooth USB Adapter /* iTegno products */ product ITEGNO WM1080A 0x1080 WM1080A GSM/GPRS modem product ITEGNO WM2080A 0x2080 WM2080A CDMA modem /* Ituner networks products */ product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20 product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20 /* Jablotron products */ product JABLOTRON PC60B 0x0001 PC-60B /* Jaton products */ product JATON EDA 0x5704 Ethernet /* Jeti products */ product JETI SPC1201 0x04b2 FTDI compatible adapter /* JMicron products */ product JMICRON JM20336 0x2336 USB to SATA Bridge product JMICRON JM20337 0x2338 USB to ATA/ATAPI Bridge /* JVC products */ product JVC GR_DX95 0x000a GR-DX95 product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet /* JRC products */ product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V /* Kamstrrup products */ product KAMSTRUP OPTICALEYE 0x0001 Optical Eye/3-wire product KAMSTRUP MBUS_250D 0x0005 M-Bus Master MultiPort 250D /* Kawatsu products */ product KAWATSU MH4000P 0x0003 MiniHub 4000P /* Keisokugiken Corp. products */ product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ /* Kensington products */ product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball product KENSINGTON TURBOBALL 0x1005 TurboBall /* Keyspan products */ product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware) product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware) product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware) product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware) product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware) product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware) product KEYSPAN USA19 0x0107 USA-19 serial Adapter product KEYSPAN USA19W 0x0108 USA-19W serial Adapter product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware) product KEYSPAN USA49W 0x010a USA-49W serial Adapter product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware) product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware) product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter product KEYSPAN USA28 0x010f USA-28 serial Adapter product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter product KEYSPAN USA18 0x0111 USA-18 serial Adapter product KEYSPAN USA18X 0x0112 USA-18X serial Adapter product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware) product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware) product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter product KEYSPAN UIA10 0x0201 UIA-10 remote control product KEYSPAN UIA11 0x0202 UIA-11 remote control /* Kingston products */ product KINGSTON XX1 0x0008 Ethernet product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet product KINGSTON HYPERX3_0 0x162b DT HyperX 3.0 /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 USB Ethernet product KLSI DUH3E10BTN 0x0009 USB Ethernet /* Kobil products */ product KOBIL CONV_B1 0x2020 FTDI compatible adapter product KOBIL CONV_KAAN 0x2021 FTDI compatible adapter /* Kodak products */ product KODAK DC220 0x0100 Digital Science DC220 product KODAK DC260 0x0110 Digital Science DC260 product KODAK DC265 0x0111 Digital Science DC265 product KODAK DC290 0x0112 Digital Science DC290 product KODAK DC240 0x0120 Digital Science DC240 product KODAK DC280 0x0130 Digital Science DC280 /* Kontron AG products */ product KONTRON DM9601 0x8101 USB Ethernet product KONTRON JP1082 0x9700 USB Ethernet /* Konica Corp. Products */ product KONICA CAMERA 0x0720 Digital Color Camera /* KYE products */ product KYE NICHE 0x0001 Niche mouse product KYE NETSCROLL 0x0003 Genius NetScroll mouse product KYE FLIGHT2000 0x1004 Flight 2000 joystick product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner /* Kyocera products */ product KYOCERA FINECAM_S3X 0x0100 Finecam S3x product KYOCERA FINECAM_S4 0x0101 Finecam S4 product KYOCERA FINECAM_S5 0x0103 Finecam S5 product KYOCERA FINECAM_L3 0x0105 Finecam L3 product KYOCERA AHK3001V 0x0203 AH-K3001V product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM product KYOCERA2 KPC680 0x180a Qualcomm Kyocera CDMA Technologies MSM /* LaCie products */ product LACIE HD 0xa601 Hard Disk product LACIE CDRW 0xa602 CD R/W /* Lake Shore Cryotronics products */ product LAKESHORE 121 0x0100 121 Current Source product LAKESHORE 218A 0x0200 218A Temperature Monitor product LAKESHORE 219 0x0201 219 Temperature Monitor product LAKESHORE 233 0x0202 233 Temperature Transmitter product LAKESHORE 235 0x0203 235 Temperature Transmitter product LAKESHORE 335 0x0300 335 Temperature Controller product LAKESHORE 336 0x0301 336 Temperature Controller product LAKESHORE 350 0x0302 350 Temperature Controller product LAKESHORE 371 0x0303 371 AC Bridge product LAKESHORE 411 0x0400 411 Handheld Gaussmeter product LAKESHORE 425 0x0401 425 Gaussmeter product LAKESHORE 455A 0x0402 455A DSP Gaussmeter product LAKESHORE 475A 0x0403 475A DSP Gaussmeter product LAKESHORE 465 0x0404 465 Gaussmeter product LAKESHORE 625A 0x0600 625A Magnet PSU product LAKESHORE 642A 0x0601 642A Magnet PSU product LAKESHORE 648 0x0602 648 Magnet PSU product LAKESHORE 737 0x0700 737 VSM Controller product LAKESHORE 776 0x0701 776 Matrix Switch /* Larsen and Brusgaard products */ product LARSENBRUSGAARD ALTITRACK 0x0001 FTDI compatible adapter /* Leadtek products */ product LEADTEK 9531 0x2101 9531 GPS /* Lenovo products */ product LENOVO GIGALAN 0x304b USB 3.0 Ethernet product LENOVO ETHERNET 0x7203 USB 2.0 Ethernet /* Lexar products */ product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader product LEXAR CF_READER 0xb002 USB CF Reader product LEXAR JUMPDRIVE 0xa833 USB Jumpdrive Flash Drive /* Lexmark products */ product LEXMARK S2450 0x0009 Optra S 2450 /* Liebert products */ product LIEBERT POWERSURE_PXT 0xffff PowerSure Personal XT product LIEBERT2 PSI1000 0x0004 UPS PSI 1000 FW:08 /* Link Instruments Inc. products */ product LINKINSTRUMENTS MSO19 0xf190 Link Instruments MSO-19 product LINKINSTRUMENTS MSO28 0xf280 Link Instruments MSO-28 product LINKINSTRUMENTS MSO28_2 0xf281 Link Instruments MSO-28 /* Linksys products */ product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 product LINKSYS USB10TX1 0x200c USB10TX product LINKSYS USB10T 0x2202 USB10T Ethernet product LINKSYS USB100TX 0x2203 USB100TX Ethernet product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA product LINKSYS USB10TA 0x2206 USB10TA Ethernet product LINKSYS USB10TX2 0x400b USB10TX product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter product LINKSYS4 USB1000 0x0039 USB1000 product LINKSYS4 WUSB100 0x0070 WUSB100 product LINKSYS4 WUSB600N 0x0071 WUSB600N product LINKSYS4 WUSB54GCV2 0x0073 WUSB54GC v2 product LINKSYS4 WUSB54GCV3 0x0077 WUSB54GC v3 product LINKSYS4 RT3070 0x0078 RT3070 product LINKSYS4 WUSB600NV2 0x0079 WUSB600N v2 /* Logilink products */ product LOGILINK DUMMY 0x0000 Dummy product product LOGILINK U2M 0x0101 LogiLink USB MIDI Cable /* Logitech products */ product LOGITECH M2452 0x0203 M2452 keyboard product LOGITECH M4848 0x0301 M4848 mouse product LOGITECH PAGESCAN 0x040f PageScan product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro product LOGITECH WEBCAMC100 0X0817 Webcam C100 product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express product LOGITECH QUICKCAM 0x0850 QuickCam product LOGITECH QUICKCAMPRO3 0x0990 QuickCam Pro 9000 product LOGITECH N43 0xc000 N43 product LOGITECH N48 0xc001 N48 mouse product LOGITECH MBA47 0xc002 M-BA47 mouse product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse product LOGITECH BD58 0xc00c BD58 mouse product LOGITECH UN58A 0xc030 iFeel Mouse product LOGITECH UN53B 0xc032 iFeel MouseMan product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme product LOGITECH WMRPAD 0xc20a WingMan RumblePad product LOGITECH WMJOY 0xc281 WingMan Force joystick product LOGITECH BB13 0xc401 USB-PS/2 Trackball product LOGITECH RK53 0xc501 Cordless mouse product LOGITECH RB6 0xc503 Cordless keyboard product LOGITECH MX700 0xc506 Cordless optical mouse product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro /* Logitec Corp. products */ product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 product LOGITEC LAN_GTJU2A 0x0160 LAN-GTJ/U2A Ethernet product LOGITEC RT2870_1 0x0162 RT2870 product LOGITEC RT2870_2 0x0163 RT2870 product LOGITEC RT2870_3 0x0164 RT2870 product LOGITEC LANW300NU2 0x0166 LAN-W300N/U2 product LOGITEC LANW150NU2 0x0168 LAN-W150N/U2 product LOGITEC LANW300NU2S 0x0169 LAN-W300N/U2S /* Longcheer Holdings, Ltd. products */ product LONGCHEER WM66 0x6061 Longcheer WM66 HSDPA product LONGCHEER W14 0x9603 Mobilcom W14 product LONGCHEER DISK 0xf000 Driver disk product LONGCHEER XSSTICK 0x9605 4G Systems XSStick P14 /* Lucent products */ product LUCENT EVALKIT 0x1001 USS-720 evaluation kit /* Luwen products */ product LUWEN EASYDISK 0x0005 EasyDisc /* Macally products */ product MACALLY MOUSE1 0x0101 mouse /* Mag-Tek products */ product MAGTEK USBSWIPE 0x0002 USB Mag Stripe Swipe Reader /* Marvell Technology Group, Ltd. products */ product MARVELL SHEEVAPLUG 0x9e8f SheevaPlug serial interface /* Matrix Orbital products */ product MATRIXORBITAL FTDI_RANGE_0100 0x0100 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0101 0x0101 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0102 0x0102 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0103 0x0103 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0104 0x0104 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0105 0x0105 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0106 0x0106 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0107 0x0107 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0108 0x0108 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0109 0x0109 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010A 0x010a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010B 0x010b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010C 0x010c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010D 0x010d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010E 0x010e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_010F 0x010f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0110 0x0110 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0111 0x0111 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0112 0x0112 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0113 0x0113 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0114 0x0114 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0115 0x0115 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0116 0x0116 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0117 0x0117 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0118 0x0118 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0119 0x0119 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011A 0x011a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011B 0x011b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011C 0x011c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011D 0x011d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011E 0x011e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_011F 0x011f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0120 0x0120 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0121 0x0121 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0122 0x0122 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0123 0x0123 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0124 0x0124 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0125 0x0125 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0126 0x0126 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0128 0x0128 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0129 0x0129 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012A 0x012a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012B 0x012b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012D 0x012d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012E 0x012e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_012F 0x012f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0130 0x0130 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0131 0x0131 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0132 0x0132 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0133 0x0133 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0134 0x0134 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0135 0x0135 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0136 0x0136 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0137 0x0137 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0138 0x0138 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0139 0x0139 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013A 0x013a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013B 0x013b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013C 0x013c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013D 0x013d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013E 0x013e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_013F 0x013f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0140 0x0140 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0141 0x0141 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0142 0x0142 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0143 0x0143 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0144 0x0144 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0145 0x0145 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0146 0x0146 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0147 0x0147 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0148 0x0148 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0149 0x0149 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014A 0x014a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014B 0x014b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014C 0x014c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014D 0x014d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014E 0x014e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_014F 0x014f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0150 0x0150 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0151 0x0151 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0152 0x0152 FTDI compatible adapter product MATRIXORBITAL MOUA 0x0153 Martrix Orbital MOU-Axxxx LCD displays product MATRIXORBITAL FTDI_RANGE_0159 0x0159 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015A 0x015a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015B 0x015b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015C 0x015c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015D 0x015d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015E 0x015e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_015F 0x015f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0160 0x0160 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0161 0x0161 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0162 0x0162 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0163 0x0163 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0164 0x0164 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0165 0x0165 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0166 0x0166 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0167 0x0167 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0168 0x0168 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0169 0x0169 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016A 0x016a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016B 0x016b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016C 0x016c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016D 0x016d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016E 0x016e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_016F 0x016f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0170 0x0170 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0171 0x0171 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0172 0x0172 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0173 0x0173 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0174 0x0174 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0175 0x0175 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0176 0x0176 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0177 0x0177 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0178 0x0178 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0179 0x0179 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017A 0x017a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017B 0x017b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017C 0x017c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017D 0x017d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017E 0x017e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_017F 0x017f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0180 0x0180 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0181 0x0181 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0182 0x0182 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0183 0x0183 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0184 0x0184 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0185 0x0185 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0186 0x0186 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0187 0x0187 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0188 0x0188 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0189 0x0189 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018A 0x018a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018B 0x018b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018C 0x018c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018D 0x018d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018E 0x018e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_018F 0x018f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0190 0x0190 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0191 0x0191 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0192 0x0192 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0193 0x0193 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0194 0x0194 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0195 0x0195 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0196 0x0196 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0197 0x0197 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0198 0x0198 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_0199 0x0199 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019A 0x019a FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019B 0x019b FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019C 0x019c FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019D 0x019d FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019E 0x019e FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_019F 0x019f FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A0 0x01a0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A1 0x01a1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A2 0x01a2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A3 0x01a3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A4 0x01a4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A5 0x01a5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A6 0x01a6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A7 0x01a7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A8 0x01a8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01A9 0x01a9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AA 0x01aa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AB 0x01ab FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AC 0x01ac FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AD 0x01ad FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AE 0x01ae FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01AF 0x01af FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B0 0x01b0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B1 0x01b1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B2 0x01b2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B3 0x01b3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B4 0x01b4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B5 0x01b5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B6 0x01b6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B7 0x01b7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B8 0x01b8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01B9 0x01b9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BA 0x01ba FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BB 0x01bb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BC 0x01bc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BD 0x01bd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BE 0x01be FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01BF 0x01bf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C0 0x01c0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C1 0x01c1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C2 0x01c2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C3 0x01c3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C4 0x01c4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C5 0x01c5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C6 0x01c6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C7 0x01c7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C8 0x01c8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01C9 0x01c9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CA 0x01ca FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CB 0x01cb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CC 0x01cc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CD 0x01cd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CE 0x01ce FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01CF 0x01cf FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D0 0x01d0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D1 0x01d1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D2 0x01d2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D3 0x01d3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D4 0x01d4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D5 0x01d5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D6 0x01d6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D7 0x01d7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D8 0x01d8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01D9 0x01d9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DA 0x01da FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DB 0x01db FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DC 0x01dc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DD 0x01dd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DE 0x01de FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01DF 0x01df FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E0 0x01e0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E1 0x01e1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E2 0x01e2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E3 0x01e3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E4 0x01e4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E5 0x01e5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E6 0x01e6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E7 0x01e7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E8 0x01e8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01E9 0x01e9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EA 0x01ea FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EB 0x01eb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EC 0x01ec FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01ED 0x01ed FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EE 0x01ee FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01EF 0x01ef FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F0 0x01f0 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F1 0x01f1 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F2 0x01f2 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F3 0x01f3 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F4 0x01f4 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F5 0x01f5 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F6 0x01f6 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F7 0x01f7 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F8 0x01f8 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01F9 0x01f9 FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FA 0x01fa FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FB 0x01fb FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FC 0x01fc FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FD 0x01fd FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FE 0x01fe FTDI compatible adapter product MATRIXORBITAL FTDI_RANGE_01FF 0x01ff FTDI compatible adapter /* MCT Corp. */ product MCT HUB0100 0x0100 Hub product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub product MCT USB232 0x0210 USB-232 Interface product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products /* Medeli */ product MEDELI DD305 0x5011 DD305 Digital Drum Set /* MediaTek, Inc. */ product MEDIATEK MTK3329 0x3329 MTK II GPS Receiver /* Meizu Electronics */ product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL) /* Melco, Inc products */ product MELCO LUATX1 0x0001 LUA-TX Ethernet product MELCO LUATX5 0x0005 LUA-TX Ethernet product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet product MELCO LUAKTX 0x0012 LUA-KTX Ethernet product MELCO DUBPXXG 0x001c DUB-PxxG product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN product MELCO KG54 0x0066 WLI-U2-KG54 WLAN product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN product MELCO LUA3U2AGT 0x006e LUA3-U2-AGT product MELCO NINWIFI 0x008b Nintendo Wi-Fi product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation product MELCO SG54HP 0x00d8 WLI-U2-SG54HP product MELCO G54HP 0x00d9 WLI-U2-G54HP product MELCO KG54L 0x00da WLI-U2-KG54L product MELCO WLIUCG300N 0x00e8 WLI-UC-G300N product MELCO SG54HG 0x00f4 WLI-U2-SG54HG product MELCO WLRUCG 0x0116 WLR-UC-G product MELCO WLRUCGAOSS 0x0119 WLR-UC-G-AOSS product MELCO WLIUCAG300N 0x012e WLI-UC-AG300N product MELCO WLIUCG 0x0137 WLI-UC-G product MELCO WLIUCG300HP 0x0148 WLI-UC-G300HP product MELCO RT2870_2 0x0150 RT2870 product MELCO WLIUCGN 0x015d WLI-UC-GN product MELCO WLIUCG301N 0x016f WLI-UC-G301N product MELCO WLIUCGNM 0x01a2 WLI-UC-GNM product MELCO WLIUCG300HPV1 0x01a8 WLI-UC-G300HP-V1 product MELCO WLIUCGNM2 0x01ee WLI-UC-GNM2 /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* MetaGeek products */ product METAGEEK TELLSTICK 0x0c30 FTDI compatible adapter product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x product METAGEEK2 WISPYDBX 0x5000 MetaGeek Wi-Spy DBx /* Metricom products */ product METRICOM RICOCHET_GS 0x0001 Ricochet GS /* MGE UPS Systems */ product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 /* MEI products */ product MEI CASHFLOW_SC 0x1100 Cashflow-SC Cash Acceptor product MEI S2000 0x1101 Series 2000 Combo Acceptor /* Microdia / Sonix Techonology Co., Ltd. products */ product CHICONY2 YUREX 0x1010 YUREX product CHICONY2 CAM_1 0x62c0 CAM_1 product CHICONY2 TEMPER 0x7401 TEMPer sensor /* Micro Star International products */ product MSI BT_DONGLE 0x1967 Bluetooth USB dongle product MSI RT3070_1 0x3820 RT3070 product MSI RT3070_2 0x3821 RT3070 product MSI RT3070_8 0x3822 RT3070 product MSI RT3070_3 0x3870 RT3070 product MSI RT3070_9 0x3871 RT3070 product MSI UB11B 0x6823 UB11B product MSI RT2570 0x6861 RT2570 product MSI RT2570_2 0x6865 RT2570 product MSI RT2570_3 0x6869 RT2570 product MSI RT2573_1 0x6874 RT2573 product MSI RT2573_2 0x6877 RT2573 product MSI RT3070_4 0x6899 RT3070 product MSI RT3070_5 0x821a RT3070 product MSI RT3070_10 0x822a RT3070 product MSI RT3070_6 0x870a RT3070 product MSI RT3070_11 0x871a RT3070 product MSI RT3070_7 0x899a RT3070 product MSI RT2573_3 0xa861 RT2573 product MSI RT2573_4 0xa874 RT2573 /* Micron products */ product MICRON REALSSD 0x0655 Real SSD eUSB /* Microsoft products */ product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite product MICROSOFT DDS80 0x0014 Digital Sound System 80 product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel product MICROSOFT INETPRO 0x001c Internet Keyboard Pro product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro product MICROSOFT INTELLIMOUSE5 0x0039 IntelliMouse 1.1 5-Button Mouse product MICROSOFT WHEELMOUSE 0x0040 Wheel Mouse Optical product MICROSOFT MN510 0x006e MN510 Wireless product MICROSOFT 700WX 0x0079 Palm 700WX product MICROSOFT MN110 0x007a 10/100 USB NIC product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023) product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043) product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049) product MICROSOFT NATURAL4000 0x00db Natural Ergonomic Keyboard 4000 product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 product MICROTECH DPCM 0x0006 USB CameraMate product MICROTECH FREECOM 0xfc01 Freecom USB-IDE /* Microtek products */ product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner product MICROTEK X6U 0x0099 ScanMaker X6 - X6U product MICROTEK C6 0x009a Phantom C6 scanner product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner product MICROTEK V6USL 0x00a3 ScanMaker V6USL product MICROTEK V6USL2 0x80a3 ScanMaker V6USL product MICROTEK V6UL 0x80ac ScanMaker V6UL /* Microtune, Inc. products */ product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle /* Midiman products */ product MAUDIO MIDISPORT2X2 0x1001 Midisport 2x2 product MAUDIO FASTTRACKULTRA 0x2080 Fast Track Ultra product MAUDIO FASTTRACKULTRA8R 0x2081 Fast Track Ultra 8R /* MindsAtWork products */ product MINDSATWORK WALLET 0x0001 Digital Wallet /* Minolta Co., Ltd. */ product MINOLTA 2300 0x4001 Dimage 2300 product MINOLTA S304 0x4007 Dimage S304 product MINOLTA X 0x4009 Dimage X product MINOLTA 5400 0x400e Dimage 5400 product MINOLTA F300 0x4011 Dimage F300 product MINOLTA E223 0x4017 Dimage E223 /* Mitsumi products */ product MITSUMI CDRRW 0x0000 CD-R/RW Drive product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle product MITSUMI FDD 0x6901 USB FDD /* Mobile Action products */ product MOBILEACTION MA620 0x0620 MA-620 Infrared Adapter /* Mobility products */ product MOBILITY USB_SERIAL 0x0202 FTDI compatible adapter product MOBILITY EA 0x0204 Ethernet product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet /* MosChip products */ product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter product MOSCHIP MCS7730 0x7730 MCS7730 Ethernet product MOSCHIP MCS7820 0x7820 MCS7820 Serial Port Adapter product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet product MOSCHIP MCS7832 0x7832 MCS7832 Ethernet product MOSCHIP MCS7840 0x7840 MCS7840 Serial Port Adapter /* Motorola products */ product MOTOROLA MC141555 0x1555 MC141555 hub controller product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem product MOTOROLA2 T720C 0x2822 T720c product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones product MOTOROLA2 E398 0x4810 E398 Mobile Phone product MOTOROLA2 USBLAN 0x600c USBLAN product MOTOROLA2 USBLAN2 0x6027 USBLAN product MOTOROLA2 MB886 0x710f MB886 Mobile Phone (Atria HD) product MOTOROLA4 RT2770 0x9031 RT2770 product MOTOROLA4 RT3070 0x9032 RT3070 /* MpMan products */ product MPMAN MPF400_2 0x25a8 MPF400 Music Player 2Go product MPMAN MPF400_1 0x36d0 MPF400 Music Player 1Go /* MultiTech products */ product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem /* Mustek products */ product MUSTEK 1200CU 0x0001 1200 CU scanner product MUSTEK 600CU 0x0002 600 CU scanner product MUSTEK 1200USB 0x0003 1200 USB scanner product MUSTEK 1200UB 0x0006 1200 UB scanner product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner product MUSTEK BEARPAW2400TA 0x0218 BearPaw 2400TA scanner product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner product MUSTEK 600USB 0x0873 600 USB scanner product MUSTEK MDC800 0xa800 MDC-800 digital camera /* M-Systems products */ product MSYSTEMS DISKONKEY 0x0010 DiskOnKey product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey /* Myson products */ product MYSON HEDEN_8813 0x8813 USB-IDE product MYSON HEDEN 0x8818 USB-IDE product MYSON HUBREADER 0x8819 COMBO Card reader with USB HUB product MYSON STARREADER 0x9920 USB flash card adapter /* National Semiconductor */ product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 /* NEC products */ product NEC HUB_0050 0x0050 USB 2.0 7-Port Hub product NEC HUB_005A 0x005a USB 2.0 4-Port Hub product NEC WL300NUG 0x0249 WL300NU-G product NEC HUB 0x55aa hub product NEC HUB_B 0x55ab hub /* NEODIO products */ product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller product NEODIO ND5010 0x5010 Multi-format Flash Controller /* Neotel products */ product NEOTEL PRIME 0x4000 Prime USB modem /* Netac products */ product NETAC CF_CARD 0x1060 USB-CF-Card product NETAC ONLYDISK 0x0003 OnlyDisk /* NetChip Technology Products */ product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect product NETCHIP CLIK_40 0xa140 USB Clik! 40 product NETCHIP GADGETZERO 0xa4a0 Linux Gadget Zero product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x product NETCHIP POCKETBOOK 0xa4a5 PocketBook /* Netgear products */ product NETGEAR EA101 0x1001 Ethernet product NETGEAR EA101X 0x1002 Ethernet product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1 product NETGEAR FA120 0x1040 USB 2.0 Ethernet product NETGEAR M4100 0x1100 M4100/M5300/M7100 series switch product NETGEAR WG111V1_2 0x4240 PrismGT USB 2.0 WLAN product NETGEAR WG111V3 0x4260 WG111v3 product NETGEAR WG111U 0x4300 WG111U product NETGEAR WG111U_NF 0x4301 WG111U (no firmware) product NETGEAR WG111V2 0x6a00 WG111V2 product NETGEAR WN111V2 0x9001 WN111V2 product NETGEAR WNDA3100 0x9010 WNDA3100 product NETGEAR WNDA4100 0x9012 WNDA4100 product NETGEAR WNDA3200 0x9018 WNDA3200 product NETGEAR RTL8192CU 0x9021 RTL8192CU product NETGEAR WNA1000 0x9040 WNA1000 product NETGEAR WNA1000M 0x9041 WNA1000M product NETGEAR2 MA101 0x4100 MA101 product NETGEAR2 MA101B 0x4102 MA101 Rev B product NETGEAR3 WG111T 0x4250 WG111T product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware) product NETGEAR3 WPN111 0x5f00 WPN111 product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware) product NETGEAR3 WPN111_2 0x5f02 WPN111 product NETGEAR4 RTL8188CU 0x9041 RTL8188CU /* NetIndex products */ product NETINDEX WS002IN 0x2001 Willcom WS002IN /* NEWlink */ product NEWLINK USB2IDEBRIDGE 0x00ff USB 2.0 Hard Drive Enclosure /* Nikon products */ product NIKON E990 0x0102 Digital Camera E990 product NIKON LS40 0x4000 CoolScan LS40 ED product NIKON D300 0x041a Digital Camera D300 /* NovaTech Products */ product NOVATECH NV902 0x9020 NovaTech NV-902W product NOVATECH RT2573 0x9021 RT2573 product NOVATECH RTL8188CU 0x9071 RTL8188CU /* Nokia products */ product NOKIA N958GB 0x0070 Nokia N95 8GBc product NOKIA2 CA42 0x1234 CA-42 cable /* Novatel Wireless products */ product NOVATEL V640 0x1100 Merlin V620 product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA product NOVATEL V620 0x1110 Merlin V620 product NOVATEL V740 0x1120 Merlin V740 product NOVATEL V720 0x1130 Merlin V720 product NOVATEL U740 0x1400 Merlin U740 product NOVATEL U740_2 0x1410 Merlin U740 product NOVATEL U870 0x1420 Merlin U870 product NOVATEL XU870 0x1430 Merlin XU870 product NOVATEL X950D 0x1450 Merlin X950D product NOVATEL ES620 0x2100 Expedite ES620 product NOVATEL E725 0x2120 Expedite E725 product NOVATEL ES620_2 0x2130 Expedite ES620 product NOVATEL ES620 0x2100 ES620 CDMA product NOVATEL U720 0x2110 Merlin U720 product NOVATEL EU730 0x2400 Expedite EU730 product NOVATEL EU740 0x2410 Expedite EU740 product NOVATEL EU870D 0x2420 Expedite EU870D product NOVATEL U727 0x4100 Merlin U727 CDMA product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA product NOVATEL MC990D 0x7001 Novatel MC990D product NOVATEL ZEROCD 0x5010 Novatel ZeroCD product NOVATEL MIFI2200V 0x5020 Novatel MiFi 2200 CDMA Virgin Mobile product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD product NOVATEL MIFI2200 0x5041 Novatel MiFi 2200 CDMA product NOVATEL U727_2 0x5100 Merlin U727 CDMA product NOVATEL U760 0x6000 Novatel U760 product NOVATEL MC760 0x6002 Novatel MC760 product NOVATEL MC547 0x7042 Novatel MC547 product NOVATEL MC679 0x7031 Novatel MC679 product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* O2Micro products */ product O2MICRO OZ776_HUB 0x7761 OZ776 hub product O2MICRO OZ776_CCID_SC 0x7772 OZ776 CCID SC Reader /* Olimex products */ product OLIMEX ARM_USB_OCD 0x0003 FTDI compatible adapter product OLIMEX ARM_USB_OCD_H 0x002b FTDI compatible adapter /* Olympus products */ product OLYMPUS C1 0x0102 C-1 Digital Camera product OLYMPUS C700 0x0105 C-700 Ultra Zoom /* OmniVision Technologies, Inc. products */ product OMNIVISION OV511 0x0511 OV511 Camera product OMNIVISION OV511PLUS 0xa511 OV511+ Camera /* OnSpec Electronic, Inc. */ product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer product ONSPEC READER 0xa003 Datafab-based Reader product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1) product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55 /* Option products */ product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard product OPTION GT3G 0x6000 GlobeTrotter 3G datacard product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem product OPTION GTMAX72 0x6711 GlobeTrotter Max 7.2 HSDPA product OPTION GTHSDPA 0x6971 GlobeTrotter HSDPA product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA product OPTION GTMAXHSUPAE 0x6901 GlobeTrotter HSUPA PCIe product OPTION GTMAX380HSUPAE 0x7211 GlobeTrotter 380HSUPA PCIe product OPTION GT3G_1 0x6050 3G modem product OPTION GT3G_2 0x6100 3G modem product OPTION GT3G_3 0x6150 3G modem product OPTION GT3G_4 0x6200 3G modem product OPTION GT3G_5 0x6250 3G modem product OPTION GT3G_6 0x6350 3G modem product OPTION E6500 0x6500 3G modem product OPTION E6501 0x6501 3G modem product OPTION E6601 0x6601 3G modem product OPTION E6721 0x6721 3G modem product OPTION E6741 0x6741 3G modem product OPTION E6761 0x6761 3G modem product OPTION E6800 0x6800 3G modem product OPTION E7021 0x7021 3G modem product OPTION E7041 0x7041 3G modem product OPTION E7061 0x7061 3G modem product OPTION E7100 0x7100 3G modem product OPTION GTM380 0x7201 3G modem product OPTION GE40X 0x7601 Globetrotter HSUPA product OPTION GSICON72 0x6911 GlobeSurfer iCON product OPTION GSICONHSUPA 0x7251 Globetrotter HSUPA product OPTION ICON401 0x7401 GlobeSurfer iCON 401 product OPTION GTHSUPA 0x7011 Globetrotter HSUPA product OPTION GMT382 0x7501 Globetrotter HSUPA product OPTION GE40X_1 0x7301 Globetrotter HSUPA product OPTION GE40X_2 0x7361 Globetrotter HSUPA product OPTION GE40X_3 0x7381 Globetrotter HSUPA product OPTION GTM661W 0x9000 GTM661W product OPTION ICONEDGE 0xc031 GlobeSurfer iCON EDGE product OPTION MODHSXPA 0xd013 Globetrotter HSUPA product OPTION ICON321 0xd031 Globetrotter HSUPA product OPTION ICON505 0xd055 Globetrotter iCON 505 product OPTION ICON452 0x7901 Globetrotter iCON 452 /* Optoelectronics Co., Ltd */ product OPTO BARCODE 0x0001 Barcode Reader product OPTO OPTICONCODE 0x0009 Opticon Code Reader product OPTO BARCODE_1 0xa002 Barcode Reader product OPTO CRD7734 0xc000 USB Cradle CRD-7734-RU product OPTO CRD7734_1 0xc001 USB Cradle CRD-7734-RU /* OvisLink product */ product OVISLINK RT3072 0x3072 RT3072 /* OQO */ product OQO WIFI01 0x0002 model 01 WiFi interface product OQO BT01 0x0003 model 01 Bluetooth interface product OQO ETHER01PLUS 0x7720 model 01+ Ethernet product OQO ETHER01 0x8150 model 01 Ethernet interface /* Ours Technology Inc. */ product OTI DKU5 0x6858 DKU-5 Serial /* Owen.ru products */ product OWEN AC4 0x0004 AC4 USB-RS485 converter /* OWL producs */ product OWL CM_160 0xca05 OWL CM-160 power monitor /* Palm Computing, Inc. product */ product PALM SERIAL 0x0080 USB Serial product PALM M500 0x0001 Palm m500 product PALM M505 0x0002 Palm m505 product PALM M515 0x0003 Palm m515 product PALM I705 0x0020 Palm i705 product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z product PALM M125 0x0040 Palm m125 product PALM M130 0x0050 Palm m130 product PALM TUNGSTEN_T 0x0060 Palm Tungsten T product PALM ZIRE31 0x0061 Palm Zire 31 product PALM ZIRE 0x0070 Palm Zire /* Panasonic products */ product PANASONIC LS120CAM 0x0901 LS-120 Camera product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW product PANASONIC SDCAAE 0x1b00 MultiMediaCard product PANASONIC TYTP50P6S 0x3900 TY-TP50P6-S 50in Touch Panel /* Papouch products */ product PAPOUCH AD4USB 0x8003 FTDI compatible adapter product PAPOUCH AP485 0x0101 FTDI compatible adapter product PAPOUCH AP485_2 0x0104 FTDI compatible adapter product PAPOUCH DRAK5 0x0700 FTDI compatible adapter product PAPOUCH DRAK6 0x1000 FTDI compatible adapter product PAPOUCH GMSR 0x8005 FTDI compatible adapter product PAPOUCH GMUX 0x8004 FTDI compatible adapter product PAPOUCH IRAMP 0x0500 FTDI compatible adapter product PAPOUCH LEC 0x0300 FTDI compatible adapter product PAPOUCH MU 0x8001 FTDI compatible adapter product PAPOUCH QUIDO10X1 0x0b00 FTDI compatible adapter product PAPOUCH QUIDO2X16 0x0e00 FTDI compatible adapter product PAPOUCH QUIDO2X2 0x0a00 FTDI compatible adapter product PAPOUCH QUIDO30X3 0x0c00 FTDI compatible adapter product PAPOUCH QUIDO3X32 0x0f00 FTDI compatible adapter product PAPOUCH QUIDO4X4 0x0900 FTDI compatible adapter product PAPOUCH QUIDO60X3 0x0d00 FTDI compatible adapter product PAPOUCH QUIDO8X8 0x0800 FTDI compatible adapter product PAPOUCH SB232 0x0301 FTDI compatible adapter product PAPOUCH SB422 0x0102 FTDI compatible adapter product PAPOUCH SB422_2 0x0105 FTDI compatible adapter product PAPOUCH SB485 0x0100 FTDI compatible adapter product PAPOUCH SB485C 0x0107 FTDI compatible adapter product PAPOUCH SB485S 0x0106 FTDI compatible adapter product PAPOUCH SB485_2 0x0103 FTDI compatible adapter product PAPOUCH SIMUKEY 0x8002 FTDI compatible adapter product PAPOUCH TMU 0x0400 FTDI compatible adapter product PAPOUCH UPSUSB 0x8000 FTDI compatible adapter /* PARA Industrial products */ product PARA RT3070 0x8888 RT3070 /* Simtec Electronics products */ product SIMTEC ENTROPYKEY 0x0001 Entropy Key /* Pegatron products */ product PEGATRON RT2870 0x0002 RT2870 product PEGATRON RT3070 0x000c RT3070 product PEGATRON RT3070_2 0x000e RT3070 product PEGATRON RT3070_3 0x0010 RT3070 /* Peracom products */ product PERACOM SERIAL1 0x0001 Serial product PERACOM ENET 0x0002 Ethernet product PERACOM ENET3 0x0003 At Home Ethernet product PERACOM ENET2 0x0005 Ethernet /* Philips products */ product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System product PHILIPS HUB 0x0201 hub product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System product PHILIPS ACE1001 0x066a AKTAKOM ACE-1001 cable product PHILIPS SPE3030CC 0x083a USB 2.0 External Disk product PHILIPS SNU5600 0x1236 SNU5600 product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player product PHILIPS RT2870 0x200f RT2870 /* Philips Semiconductor products */ product PHILIPSSEMI HUB1122 0x1122 HUB /* Megatec */ product MEGATEC UPS 0x5161 Phoenixtec protocol based UPS /* P.I. Engineering products */ product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter /* Planex Communications products */ product PLANEX GW_US11H 0x14ea GW-US11H WLAN product PLANEX2 RTL8188CUS 0x1201 RTL8188CUS product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN product PLANEX2 GW_US300 0x5304 GW-US300 product PLANEX2 RTL8188CU_1 0xab2a RTL8188CU product PLANEX2 RTL8188CU_2 0xed17 RTL8188CU product PLANEX2 RTL8188CU_3 0x4902 RTL8188CU product PLANEX2 RTL8188CU_4 0xab2e RTL8188CU product PLANEX2 RTL8192CU 0xab2b RTL8192CU product PLANEX2 GWUS54HP 0xab01 GW-US54HP product PLANEX2 GWUS300MINIS 0xab24 GW-US300MiniS product PLANEX2 RT3070 0xab25 RT3070 product PLANEX2 MZKUE150N 0xab2f MZK-UE150N product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2 product PLANEX2 GWUS54SG 0xc002 GW-US54SG product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL product PLANEX2 GWUS54GD 0xed01 GW-US54GD product PLANEX2 GWUSMM 0xed02 GW-USMM product PLANEX2 RT2870 0xed06 RT2870 product PLANEX2 GWUSMICRON 0xed14 GW-USMicroN product PLANEX2 GWUSVALUEEZ 0xed17 GW-USValue-EZ product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ product PLANEX3 GU1000T 0xab11 GU-1000T product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini product PLANEX2 GWUSNANO 0xab28 GW-USNano /* Plextor Corp. */ product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U /* PLX products */ product PLX TESTBOARD 0x9060 test board product PLX CA42 0xac70 CA-42 /* PNY products */ product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive /* PortGear products */ product PORTGEAR EA8 0x0008 Ethernet product PORTGEAR EA9 0x0009 Ethernet /* Portsmith products */ product PORTSMITH EEA 0x3003 Express Ethernet /* Posiflex products */ product POSIFLEX PP7000 0x0300 FTDI compatible adapter /* Primax products */ product PRIMAX G2X300 0x0300 G2-200 scanner product PRIMAX G2E300 0x0301 G2E-300 scanner product PRIMAX G2300 0x0302 G2-300 scanner product PRIMAX G2E3002 0x0303 G2E-300 scanner product PRIMAX 9600 0x0340 Colorado USB 9600 scanner product PRIMAX 600U 0x0341 Colorado 600u scanner product PRIMAX 6200 0x0345 Visioneer 6200 scanner product PRIMAX 19200 0x0360 Colorado USB 19200 scanner product PRIMAX 1200U 0x0361 Colorado 1200u scanner product PRIMAX G600 0x0380 G2-600 scanner product PRIMAX 636I 0x0381 ReadyScan 636i product PRIMAX G2600 0x0382 G2-600 scanner product PRIMAX G2E600 0x0383 G2E-600 scanner product PRIMAX COMFORT 0x4d01 Comfort product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 product PRIMAX HP_RH304AA 0x4d17 HP RH304AA mouse /* Prolific products */ product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface product PROLIFIC MOTOROLA 0x0307 Motorola Cable product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) product PROLIFIC ALLTRONIX_GPRS 0x0609 Alltronix ACM003U00 modem product PROLIFIC ALDIGA_AL11U 0x0611 AlDiga AL-11U modem product PROLIFIC MICROMAX_610U 0x0612 Micromax 610U product PROLIFIC DCU11 0x1234 DCU-11 Phone Cable product PROLIFIC UIC_MSR206 0x206a UIC MSR206 Card Reader product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) product PROLIFIC PL2305 0x2305 Parallel printer product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface product PROLIFIC PL2506 0x2506 PL2506 USB to IDE Bridge product PROLIFIC HCR331 0x331a HCR331 Hybrid Card Reader product PROLIFIC PHAROS 0xaaa0 Prolific Pharos product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3) product PROLIFIC2 PL2303 0x2303 PL2303 Serial Adapter /* Putercom products */ product PUTERCOM UPA100 0x047e USB-1284 BRIDGE /* Qcom products */ product QCOM RT2573 0x6196 RT2573 product QCOM RT2573_2 0x6229 RT2573 product QCOM RT2573_3 0x6238 RT2573 product QCOM RT2870 0x6259 RT2870 /* QI-hardware */ product QIHARDWARE JTAGSERIAL 0x0713 FTDI compatible adapter /* Qisda products */ product QISDA H21_1 0x4512 3G modem product QISDA H21_2 0x4523 3G modem product QISDA H20_1 0x4515 3G modem product QISDA H20_2 0x4519 3G modem /* Qualcomm products */ product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone product QUALCOMM NTT_L02C_MODEM 0x618f NTT DOCOMO L-02C product QUALCOMM NTT_L02C_STORAGE 0x61dd NTT DOCOMO L-02C product QUALCOMM2 MF330 0x6613 MF330 product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem product QUALCOMM2 AC8700 0x6000 AC8700 product QUALCOMM2 VW110L 0x1000 Vertex Wireless 110L modem product QUALCOMM2 SIM5218 0x9000 SIM5218 product QUALCOMM2 WM620 0x9002 Neoway WM620 product QUALCOMM2 GOBI2000_QDL 0x9204 Qualcomm Gobi 2000 QDL product QUALCOMM2 GOBI2000 0x9205 Qualcomm Gobi 2000 modem product QUALCOMM2 VT80N 0x6500 Venus VT80N product QUALCOMM3 VFAST2 0x9909 Venus Fast2 modem product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem product QUALCOMMINC E0002 0x0002 3G modem product QUALCOMMINC E0003 0x0003 3G modem product QUALCOMMINC E0004 0x0004 3G modem product QUALCOMMINC E0005 0x0005 3G modem product QUALCOMMINC E0006 0x0006 3G modem product QUALCOMMINC E0007 0x0007 3G modem product QUALCOMMINC E0008 0x0008 3G modem product QUALCOMMINC E0009 0x0009 3G modem product QUALCOMMINC E000A 0x000a 3G modem product QUALCOMMINC E000B 0x000b 3G modem product QUALCOMMINC E000C 0x000c 3G modem product QUALCOMMINC E000D 0x000d 3G modem product QUALCOMMINC E000E 0x000e 3G modem product QUALCOMMINC E000F 0x000f 3G modem product QUALCOMMINC E0010 0x0010 3G modem product QUALCOMMINC E0011 0x0011 3G modem product QUALCOMMINC E0012 0x0012 3G modem product QUALCOMMINC E0013 0x0013 3G modem product QUALCOMMINC E0014 0x0014 3G modem product QUALCOMMINC MF628 0x0015 3G modem product QUALCOMMINC MF633R 0x0016 ZTE WCDMA modem product QUALCOMMINC E0017 0x0017 3G modem product QUALCOMMINC E0018 0x0018 3G modem product QUALCOMMINC E0019 0x0019 3G modem product QUALCOMMINC E0020 0x0020 3G modem product QUALCOMMINC E0021 0x0021 3G modem product QUALCOMMINC E0022 0x0022 3G modem product QUALCOMMINC E0023 0x0023 3G modem product QUALCOMMINC E0024 0x0024 3G modem product QUALCOMMINC E0025 0x0025 3G modem product QUALCOMMINC E0026 0x0026 3G modem product QUALCOMMINC E0027 0x0027 3G modem product QUALCOMMINC E0028 0x0028 3G modem product QUALCOMMINC E0029 0x0029 3G modem product QUALCOMMINC E0030 0x0030 3G modem product QUALCOMMINC MF626 0x0031 3G modem product QUALCOMMINC E0032 0x0032 3G modem product QUALCOMMINC E0033 0x0033 3G modem product QUALCOMMINC E0037 0x0037 3G modem product QUALCOMMINC E0039 0x0039 3G modem product QUALCOMMINC E0042 0x0042 3G modem product QUALCOMMINC E0043 0x0043 3G modem product QUALCOMMINC E0048 0x0048 3G modem product QUALCOMMINC E0049 0x0049 3G modem product QUALCOMMINC E0051 0x0051 3G modem product QUALCOMMINC E0052 0x0052 3G modem product QUALCOMMINC ZTE_STOR2 0x0053 USB ZTE Storage product QUALCOMMINC E0054 0x0054 3G modem product QUALCOMMINC E0055 0x0055 3G modem product QUALCOMMINC E0057 0x0057 3G modem product QUALCOMMINC E0058 0x0058 3G modem product QUALCOMMINC E0059 0x0059 3G modem product QUALCOMMINC E0060 0x0060 3G modem product QUALCOMMINC E0061 0x0061 3G modem product QUALCOMMINC E0062 0x0062 3G modem product QUALCOMMINC E0063 0x0063 3G modem product QUALCOMMINC E0064 0x0064 3G modem product QUALCOMMINC E0066 0x0066 3G modem product QUALCOMMINC E0069 0x0069 3G modem product QUALCOMMINC E0070 0x0070 3G modem product QUALCOMMINC E0073 0x0073 3G modem product QUALCOMMINC E0076 0x0076 3G modem product QUALCOMMINC E0078 0x0078 3G modem product QUALCOMMINC E0082 0x0082 3G modem product QUALCOMMINC E0086 0x0086 3G modem product QUALCOMMINC MF112 0x0103 3G modem product QUALCOMMINC SURFSTICK 0x0117 1&1 Surf Stick product QUALCOMMINC K3772_Z_INIT 0x1179 K3772-Z Initial product QUALCOMMINC K3772_Z 0x1181 K3772-Z product QUALCOMMINC ZTE_MF730M 0x1420 3G modem product QUALCOMMINC MF195E_INIT 0x1514 MF195E initial product QUALCOMMINC MF195E 0x1516 MF195E product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage product QUALCOMMINC E2002 0x2002 3G modem product QUALCOMMINC E2003 0x2003 3G modem product QUALCOMMINC AC682 0xffdd CDMA 1xEVDO USB modem product QUALCOMMINC AC682_INIT 0xffde CDMA 1xEVDO USB modem (initial) product QUALCOMMINC AC8710 0xfff1 3G modem product QUALCOMMINC AC2726 0xfff5 3G modem product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem /* Quanta products */ product QUANTA RW6815_1 0x00ce HP iPAQ rw6815 product QUANTA RT3070 0x0304 RT3070 product QUANTA Q101_STOR 0x1000 USB Q101 Storage product QUANTA Q101 0xea02 HSDPA modem product QUANTA Q111 0xea03 HSDPA modem product QUANTA GLX 0xea04 HSDPA modem product QUANTA GKE 0xea05 HSDPA modem product QUANTA GLE 0xea06 HSDPA modem product QUANTA RW6815R 0xf003 HP iPAQ rw6815 RNDIS /* Qtronix products */ product QTRONIX 980N 0x2011 Scorpion-980N keyboard /* Quickshot products */ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad /* Radio Shack */ product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable /* Rainbow Technologies products */ product RAINBOW IKEY2000 0x1200 i-Key 2000 /* Ralink Technology products */ product RALINK RT2570 0x1706 RT2500USB Wireless Adapter product RALINK RT2070 0x2070 RT2070 product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter product RALINK RT2573 0x2573 RT2501USB Wireless Adapter product RALINK RT2671 0x2671 RT2601USB Wireless Adapter product RALINK RT2770 0x2770 RT2770 product RALINK RT2870 0x2870 RT2870 product RALINK RT_STOR 0x2878 USB Storage product RALINK RT3070 0x3070 RT3070 product RALINK RT3071 0x3071 RT3071 product RALINK RT3072 0x3072 RT3072 product RALINK RT3370 0x3370 RT3370 product RALINK RT3572 0x3572 RT3572 product RALINK RT3573 0x3573 RT3573 product RALINK RT5370 0x5370 RT5370 product RALINK RT5572 0x5572 RT5572 product RALINK RT8070 0x8070 RT8070 product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter /* RATOC Systems products */ product RATOC REXUSB60 0xb000 USB serial adapter REX-USB60 product RATOC REXUSB60F 0xb020 USB serial adapter REX-USB60F /* Realtek products */ /* Green House and CompUSA OEM this part */ product REALTEK DUMMY 0x0000 Dummy product product REALTEK USB20CRW 0x0158 USB20CRW Card Reader product REALTEK RTL8188ETV 0x0179 RTL8188ETV product REALTEK RTL8188CTV 0x018a RTL8188CTV product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet product REALTEK RTL8152 0x8152 RTL8152 USB Ethernet product REALTEK RTL8153 0x8153 RTL8153 USB Ethernet product REALTEK RTL8188CE_0 0x8170 RTL8188CE product REALTEK RTL8171 0x8171 RTL8171 product REALTEK RTL8172 0x8172 RTL8172 product REALTEK RTL8173 0x8173 RTL8173 product REALTEK RTL8174 0x8174 RTL8174 product REALTEK RTL8188CU_0 0x8176 RTL8188CU product REALTEK RTL8188EU 0x8179 RTL8188EU product REALTEK RTL8188CE_1 0x817e RTL8188CE product REALTEK RTL8188CU_1 0x817a RTL8188CU product REALTEK RTL8188CU_2 0x817b RTL8188CU product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter product REALTEK RTL8187B_0 0x8189 RTL8187B Wireless Adapter product REALTEK RTL8188CU_3 0x8191 RTL8188CU product REALTEK RTL8196EU 0x8196 RTL8196EU product REALTEK RTL8187B_1 0x8197 RTL8187B Wireless Adapter product REALTEK RTL8187B_2 0x8198 RTL8187B Wireless Adapter product REALTEK RTL8188CUS 0x818a RTL8188CUS product REALTEK RTL8188CU_COMBO 0x8754 RTL8188CU product REALTEK RTL8191CU 0x8177 RTL8191CU product REALTEK RTL8192CU 0x8178 RTL8192CU product REALTEK RTL8192CE 0x817c RTL8192CE product REALTEK RTL8188RU_1 0x817d RTL8188RU product REALTEK RTL8188RU_3 0x817f RTL8188RU product REALTEK RTL8712 0x8712 RTL8712 product REALTEK RTL8713 0x8712 RTL8713 product REALTEK RTL8188RU_2 0x317f RTL8188RU product REALTEK RTL8192SU 0xc512 RTL8192SU /* RedOctane products */ product REDOCTANE DUMMY 0x0000 Dummy product product REDOCTANE GHMIDI 0x474b GH MIDI INTERFACE /* Renesas products */ product RENESAS RX610 0x0053 RX610 RX-Stick /* Ricoh products */ product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera product RICOH VGPVCC7 0x183a VGP-VCC7 Camera product RICOH VGPVCC8 0x183b VGP-VCC8 Camera /* Reiner-SCT products */ product REINERSCT CYBERJACK_ECOM 0x0100 e-com cyberJack /* Roland products */ product ROLAND UA100 0x0000 UA-100 Audio I/F product ROLAND UM4 0x0002 UM-4 MIDI I/F product ROLAND SC8850 0x0003 SC-8850 MIDI Synth product ROLAND U8 0x0004 U-8 Audio I/F product ROLAND UM2 0x0005 UM-2 MIDI I/F product ROLAND SC8820 0x0007 SC-8820 MIDI Synth product ROLAND PC300 0x0008 PC-300 MIDI Keyboard product ROLAND UM1 0x0009 UM-1 MIDI I/F product ROLAND SK500 0x000b SK-500 MIDI Keyboard product ROLAND SCD70 0x000c SC-D70 MIDI Synth product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) product ROLAND SD90 0x0016 SD-90 MIDI Synth product ROLAND UM550 0x0023 UM-550 MIDI I/F product ROLAND SD20 0x0027 SD-20 MIDI Synth product ROLAND SD80 0x0029 SD-80 MIDI Synth product ROLAND UA700 0x002b UA-700 Audio I/F /* Rockfire products */ product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB /* RATOC Systems products */ product RATOC REXUSB60 0xb000 REX-USB60 product RATOC REXUSB60F 0xb020 REX-USB60F /* RT system products */ product RTSYSTEMS CT29B 0x9e54 FTDI compatible adapter product RTSYSTEMS SERIAL_VX7 0x9e52 FTDI compatible adapter /* Sagem products */ product SAGEM USBSERIAL 0x0027 USB-Serial Controller product SAGEM XG760A 0x004a XG-760A product SAGEM XG76NA 0x0062 XG-76NA /* Samsung products */ product SAMSUNG WIS09ABGN 0x2018 WIS09ABGN Wireless LAN adapter product SAMSUNG ML6060 0x3008 ML-6060 laser printer product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player product SAMSUNG YP_U4 0x5092 YP-U4 MP3 Player product SAMSUNG I500 0x6601 I500 Palm USB Phone product SAMSUNG I330 0x8001 I330 phone cradle product SAMSUNG2 RT2870_1 0x2018 RT2870 /* Samsung Techwin products */ product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410 /* SanDisk products */ product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a product SANDISK SDDR31 0x0002 ImageMate SDDR-31 product SANDISK SDDR05 0x0005 ImageMate SDDR-05 product SANDISK SDDR12 0x0100 ImageMate SDDR-12 product SANDISK SDDR09 0x0200 ImageMate SDDR-09 product SANDISK SDDR75 0x0810 ImageMate SDDR-75 product SANDISK SDCZ2_128 0x7100 Cruzer Mini 128MB product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB product SANDISK IMAGEMATE_SDDR289 0xb6ba ImageMate SDDR-289 /* Sanwa Electric Instrument Co., Ltd. products */ product SANWA KB_USB2 0x0701 KB-USB2 multimeter cable /* Sanyo Electric products */ product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone /* ScanLogic products */ product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner /* Schweitzer Engineering Laboratories products */ product SEL C662 0x0001 C662 Cable /* Sealevel products */ product SEALEVEL 2101 0x2101 FTDI compatible adapter product SEALEVEL 2102 0x2102 FTDI compatible adapter product SEALEVEL 2103 0x2103 FTDI compatible adapter product SEALEVEL 2104 0x2104 FTDI compatible adapter product SEALEVEL 2106 0x9020 FTDI compatible adapter product SEALEVEL 2201_1 0x2211 FTDI compatible adapter product SEALEVEL 2201_2 0x2221 FTDI compatible adapter product SEALEVEL 2202_1 0x2212 FTDI compatible adapter product SEALEVEL 2202_2 0x2222 FTDI compatible adapter product SEALEVEL 2203_1 0x2213 FTDI compatible adapter product SEALEVEL 2203_2 0x2223 FTDI compatible adapter product SEALEVEL 2401_1 0x2411 FTDI compatible adapter product SEALEVEL 2401_2 0x2421 FTDI compatible adapter product SEALEVEL 2401_3 0x2431 FTDI compatible adapter product SEALEVEL 2401_4 0x2441 FTDI compatible adapter product SEALEVEL 2402_1 0x2412 FTDI compatible adapter product SEALEVEL 2402_2 0x2422 FTDI compatible adapter product SEALEVEL 2402_3 0x2432 FTDI compatible adapter product SEALEVEL 2402_4 0x2442 FTDI compatible adapter product SEALEVEL 2403_1 0x2413 FTDI compatible adapter product SEALEVEL 2403_2 0x2423 FTDI compatible adapter product SEALEVEL 2403_3 0x2433 FTDI compatible adapter product SEALEVEL 2403_4 0x2443 FTDI compatible adapter product SEALEVEL 2801_1 0x2811 FTDI compatible adapter product SEALEVEL 2801_2 0x2821 FTDI compatible adapter product SEALEVEL 2801_3 0x2831 FTDI compatible adapter product SEALEVEL 2801_4 0x2841 FTDI compatible adapter product SEALEVEL 2801_5 0x2851 FTDI compatible adapter product SEALEVEL 2801_6 0x2861 FTDI compatible adapter product SEALEVEL 2801_7 0x2871 FTDI compatible adapter product SEALEVEL 2801_8 0x2881 FTDI compatible adapter product SEALEVEL 2802_1 0x2812 FTDI compatible adapter product SEALEVEL 2802_2 0x2822 FTDI compatible adapter product SEALEVEL 2802_3 0x2832 FTDI compatible adapter product SEALEVEL 2802_4 0x2842 FTDI compatible adapter product SEALEVEL 2802_5 0x2852 FTDI compatible adapter product SEALEVEL 2802_6 0x2862 FTDI compatible adapter product SEALEVEL 2802_7 0x2872 FTDI compatible adapter product SEALEVEL 2802_8 0x2882 FTDI compatible adapter product SEALEVEL 2803_1 0x2813 FTDI compatible adapter product SEALEVEL 2803_2 0x2823 FTDI compatible adapter product SEALEVEL 2803_3 0x2833 FTDI compatible adapter product SEALEVEL 2803_4 0x2843 FTDI compatible adapter product SEALEVEL 2803_5 0x2853 FTDI compatible adapter product SEALEVEL 2803_6 0x2863 FTDI compatible adapter product SEALEVEL 2803_7 0x2873 FTDI compatible adapter product SEALEVEL 2803_8 0x2883 FTDI compatible adapter /* Senao products */ product SENAO RT2870_3 0x0605 RT2870 product SENAO RT2870_4 0x0615 RT2870 product SENAO NUB8301 0x2000 NUB-8301 product SENAO RT2870_1 0x9701 RT2870 product SENAO RT2870_2 0x9702 RT2870 product SENAO RT3070 0x9703 RT3070 product SENAO RT3071 0x9705 RT3071 product SENAO RT3072_1 0x9706 RT3072 product SENAO RT3072_2 0x9707 RT3072 product SENAO RT3072_3 0x9708 RT3072 product SENAO RT3072_4 0x9709 RT3072 product SENAO RT3072_5 0x9801 RT3072 product SENAO RTL8192SU_1 0x9603 RTL8192SU product SENAO RTL8192SU_2 0x9605 RTL8192SU /* ShanTou products */ product SHANTOU ST268 0x0268 ST268 product SHANTOU DM9601 0x9601 DM 9601 product SHANTOU ADM8515 0x8515 ADM8515 /* Shark products */ product SHARK PA 0x0400 Pocket Adapter /* Sharp products */ product SHARP SL5500 0x8004 Zaurus SL-5500 PDA product SHARP SLA300 0x8005 Zaurus SL-A300 PDA product SHARP SL5600 0x8006 Zaurus SL-5600 PDA product SHARP SLC700 0x8007 Zaurus SL-C700 PDA product SHARP SLC750 0x9031 Zaurus SL-C750 PDA product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone product SHARP WZERO3ADES 0x91ac Advanced W-ZERO3 ES Smartphone product SHARP WILLCOM03 0x9242 WILLCOM03 /* Shuttle Technology products */ product SHUTTLE EUSB 0x0001 E-USB Bridge product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter product SHUTTLE HIFD 0x0007 Sony Hifd product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter product SHUTTLE CF 0x000a eUSB CompactFlash Adapter product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge product SHUTTLE CDRW 0x0101 CD-RW Device product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader /* Siemens products */ product SIEMENS SPEEDSTREAM 0x1001 SpeedStream product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022 product SIEMENS2 WLL013 0x001b WLL013 product SIEMENS2 ES75 0x0034 GSM module MC35 product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter product SIEMENS3 SX1 0x0001 SX1 product SIEMENS3 X65 0x0003 X65 product SIEMENS3 X75 0x0004 X75 product SIEMENS3 EF81 0x0005 EF81 /* Sierra Wireless products */ product SIERRA EM5625 0x0017 EM5625 product SIERRA MC5720_2 0x0018 MC5720 product SIERRA MC5725 0x0020 MC5725 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E product SIERRA EM5725 0x0022 EM5725 product SIERRA C597 0x0023 Sierra Wireless Compass 597 product SIERRA MC5727 0x0024 MC5727 product SIERRA T598 0x0025 T598 product SIERRA T11 0x0026 T11 product SIERRA AC402 0x0027 AC402 product SIERRA MC5728 0x0028 MC5728 product SIERRA E0029 0x0029 E0029 product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA MC5720 0x0218 MC5720 Wireless Modem product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275 product SIERRA MC5727_2 0x0224 MC5727 product SIERRA MC8755_2 0x6802 MC8755 product SIERRA MC8765 0x6803 MC8765 product SIERRA MC8755 0x6804 MC8755 product SIERRA MC8765_2 0x6805 MC8765 product SIERRA MC8755_4 0x6808 MC8755 product SIERRA MC8765_3 0x6809 MC8765 product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem product SIERRA MC8755_3 0x6813 MC8755 HSDPA product SIERRA MC8775_2 0x6815 MC8775 product SIERRA MC8775 0x6816 MC8775 product SIERRA AC875 0x6820 Sierra Wireless AirCard 875 product SIERRA AC875U_2 0x6821 AC875U product SIERRA AC875E 0x6822 AC875E product SIERRA MC8780 0x6832 MC8780 product SIERRA MC8781 0x6833 MC8781 product SIERRA MC8780_2 0x6834 MC8780 product SIERRA MC8781_2 0x6835 MC8781 product SIERRA MC8780_3 0x6838 MC8780 product SIERRA MC8781_3 0x6839 MC8781 product SIERRA MC8785 0x683A MC8785 product SIERRA MC8785_2 0x683B MC8785 product SIERRA MC8790 0x683C MC8790 product SIERRA MC8791 0x683D MC8791 product SIERRA MC8792 0x683E MC8792 product SIERRA AC880 0x6850 Sierra Wireless AirCard 880 product SIERRA AC881 0x6851 Sierra Wireless AirCard 881 product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U product SIERRA AC885E 0x6859 AC885E product SIERRA AC885E_2 0x685A AC885E product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U product SIERRA C888 0x6890 C888 product SIERRA C22 0x6891 C22 product SIERRA E6892 0x6892 E6892 product SIERRA E6893 0x6893 E6893 product SIERRA MC8700 0x68A3 MC8700 product SIERRA MC7354 0x68C0 MC7354 product SIERRA MC7355 0x9041 MC7355 product SIERRA AC313U 0x68aa Sierra Wireless AirCard 313U product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer /* Sigmatel products */ product SIGMATEL WBT_3052 0x4200 WBT-3052 IrDA/USB Bridge product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player /* SIIG products */ /* Also: Omnidirectional Control Technology products */ product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader product SIIG WINTERREADER 0x0330 WINTERREADER Reader product SIIG2 DK201 0x0103 FTDI compatible adapter product SIIG2 USBTOETHER 0x0109 USB TO Ethernet product SIIG2 US2308 0x0421 Serial /* Silicom products */ product SILICOM U2E 0x0001 U2E product SILICOM GPE 0x0002 Psion Gold Port Ethernet /* SI Labs */ product SILABS VSTABI 0x0f91 VStabi Controller product SILABS ARKHAM_DS101_M 0x1101 Arkham DS101 Monitor product SILABS ARKHAM_DS101_A 0x1601 Arkham DS101 Adapter product SILABS BSM7DUSB 0x800a SPORTident BSM7-D USB product SILABS POLOLU 0x803b Pololu Serial product SILABS CYGNAL_DEBUG 0x8044 Cygnal Debug Adapter product SILABS SB_PARAMOUNT_ME 0x8043 Software Bisque Paramount ME product SILABS SAEL 0x8053 SA-EL USB product SILABS GSM2228 0x8054 Enfora GSM2228 USB product SILABS ARGUSISP 0x8066 Argussoft ISP product SILABS IMS_USB_RS422 0x806f IMS USB-RS422 product SILABS CRUMB128 0x807a Crumb128 board product SILABS OPTRIS_MSPRO 0x80c4 Optris MSpro LT Thermometer product SILABS DEGREE 0x80ca Degree Controls Inc product SILABS TRACIENT 0x80dd Tracient RFID product SILABS TRAQMATE 0x80ed Track Systems Traqmate product SILABS SUUNTO 0x80f6 Suunto Sports Instrument product SILABS ARYGON_MIFARE 0x8115 Arygon Mifare RFID reader product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile product SILABS TAMSMASTER 0x813f Tams Master Easy Control product SILABS WMRBATT 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGBLASTER 0x814a WMR RIGblaster Plug&Play product SILABS WMRRIGTALK 0x814b WMR RIGtalk RT1 product SILABS B_G_H3000 0x8156 B&G H3000 Data Cable product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM product SILABS HAMLINKUSB 0x815f Timewave HamLinkUSB product SILABS AVIT_USB_TTL 0x818b AVIT Research USB-TTL product SILABS MJS_TOSLINK 0x819f MJS USB-TOSLINK product SILABS WAVIT 0x81a6 ThinkOptics WavIt product SILABS MULTIPLEX_RC 0x81a9 Multiplex RC adapter product SILABS MSD_DASHHAWK 0x81ac MSD DashHawk product SILABS INSYS_MODEM 0x81ad INSYS Modem product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN product SILABS AEROCOMM 0x81e7 Aerocomm Radio product SILABS ZEPHYR_BIO 0x81e8 Zephyr Bioharness product SILABS EMS_C1007 0x81f2 EMS C1007 HF RFID controller product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1 product SILABS C2_EDGE_MODEM 0x822b Commander 2 EDGE(GSM) Modem product SILABS CYGNAL_GPS 0x826b Cygnal Fasttrax GPS product SILABS TELEGESIS_ETRX2 0x8293 Telegesis ETRX2USB product SILABS PROCYON_AVS 0x82f9 Procyon AVS product SILABS MC35PU 0x8341 MC35pu product SILABS CYGNAL 0x8382 Cygnal product SILABS AMBER_AMB2560 0x83a8 Amber Wireless AMB2560 product SILABS DEKTEK_DTAPLUS 0x83d8 DekTec DTA Plus VHF/UHF Booster product SILABS KYOCERA_GPS 0x8411 Kyocera GPS product SILABS IRZ_SG10 0x8418 IRZ SG-10 GSM/GPRS Modem product SILABS BEI_VCP 0x846e BEI USB Sensor (VCP) product SILABS BALLUFF_RFID 0x8477 Balluff RFID reader product SILABS AC_SERV_IBUS 0x85ea AC-Services IBUS Interface product SILABS AC_SERV_CIS 0x85eb AC-Services CIS-IBUS product SILABS V_PREON32 0x85f8 Virtenio Preon32 product SILABS AC_SERV_CAN 0x8664 AC-Services CAN Interface product SILABS AC_SERV_OBD 0x8665 AC-Services OBD Interface product SILABS MMB_ZIGBEE 0x88a4 MMB Networks ZigBee product SILABS INGENI_ZIGBEE 0x88a5 Planet Innovation Ingeni ZigBee product SILABS CP2102 0xea60 SILABS USB UART product SILABS CP210X_2 0xea61 CP210x Serial product SILABS CP210X_3 0xea70 CP210x Serial product SILABS CP210X_4 0xea80 CP210x Serial product SILABS INFINITY_MIC 0xea71 Infinity GPS-MIC-1 Radio Monophone product SILABS USBSCOPE50 0xf001 USBscope50 product SILABS USBWAVE12 0xf002 USBwave12 product SILABS USBPULSE100 0xf003 USBpulse100 product SILABS USBCOUNT50 0xf004 USBcount50 product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone product SILABS3 GPRS_MODEM 0xea61 GPRS Modem product SILABS4 100EU_MODEM 0xea61 GPRS Modem 100EU /* Silicon Portals Inc. */ product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) product SILICONPORTALS YAPPHONE 0x0201 YAP Phone /* Sirius Technologies products */ product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB /* Sitecom products */ product SITECOM LN029 0x182d USB 2.0 Ethernet product SITECOM SERIAL 0x2068 USB to serial cable (v2) product SITECOM2 WL022 0x182d WL-022 /* Sitecom Europe products */ product SITECOMEU RT2870_1 0x0017 RT2870 product SITECOMEU WL168V1 0x000d WL-168 v1 product SITECOMEU LN030 0x0021 MCS7830 product SITECOMEU WL168V4 0x0028 WL-168 v4 product SITECOMEU RT2870_2 0x002b RT2870 product SITECOMEU RT2870_3 0x002c RT2870 product SITECOMEU RT2870_4 0x002d RT2870 product SITECOMEU RT2770 0x0039 RT2770 product SITECOMEU RT3070_2 0x003b RT3070 product SITECOMEU RT3070_3 0x003c RT3070 product SITECOMEU RT3070_4 0x003d RT3070 product SITECOMEU RT3070 0x003e RT3070 product SITECOMEU WL608 0x003f WL-608 product SITECOMEU RT3071 0x0040 RT3071 product SITECOMEU RT3072_1 0x0041 RT3072 product SITECOMEU RT3072_2 0x0042 RT3072 product SITECOMEU WL353 0x0045 WL-353 product SITECOMEU RT3072_3 0x0047 RT3072 product SITECOMEU RT3072_4 0x0048 RT3072 product SITECOMEU RT3072_5 0x004a RT3072 product SITECOMEU WL349V1 0x004b WL-349 v1 product SITECOMEU RT3072_6 0x004d RT3072 product SITECOMEU RTL8188CU_1 0x0052 RTL8188CU product SITECOMEU RTL8188CU_2 0x005c RTL8188CU product SITECOMEU RTL8192CU 0x0061 RTL8192CU product SITECOMEU LN032 0x0072 LN-032 +product SITECOMEU LN031 0x0056 LN-031 product SITECOMEU LN028 0x061c LN-028 product SITECOMEU WL113 0x9071 WL-113 product SITECOMEU ZD1211B 0x9075 ZD1211B product SITECOMEU WL172 0x90ac WL-172 product SITECOMEU WL113R2 0x9712 WL-113 rev 2 /* Skanhex Technology products */ product SKANHEX MD_7425 0x410a MD 7425 Camera product SKANHEX SX_520Z 0x5200 SX 520z Camera /* Smart Technologies products */ product SMART PL2303 0x2303 Serial adapter /* SmartBridges products */ product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet /* SMC products */ product SMC 2102USB 0x0100 10Mbps Ethernet product SMC 2202USB 0x0200 10/100 Ethernet product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect Wireless Adapter product SMC2 2020HUB 0x2020 USB Hub product SMC2 2514HUB 0x2514 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless product SMC2 LAN9500_ETH 0x9500 USB/Ethernet product SMC2 LAN9505_ETH 0x9505 USB/Ethernet product SMC2 LAN9530_ETH 0x9530 USB/Ethernet product SMC2 LAN9730_ETH 0x9730 USB/Ethernet product SMC2 LAN9500_SAL10 0x9900 USB/Ethernet product SMC2 LAN9505_SAL10 0x9901 USB/Ethernet product SMC2 LAN9500A_SAL10 0x9902 USB/Ethernet product SMC2 LAN9505A_SAL10 0x9903 USB/Ethernet product SMC2 LAN9514_SAL10 0x9904 USB/Ethernet product SMC2 LAN9500A_HAL 0x9905 USB/Ethernet product SMC2 LAN9505A_HAL 0x9906 USB/Ethernet product SMC2 LAN9500_ETH_2 0x9907 USB/Ethernet product SMC2 LAN9500A_ETH_2 0x9908 USB/Ethernet product SMC2 LAN9514_ETH_2 0x9909 USB/Ethernet product SMC2 LAN9500A_ETH 0x9e00 USB/Ethernet product SMC2 LAN9505A_ETH 0x9e01 USB/Ethernet product SMC2 LAN89530_ETH 0x9e08 USB/Ethernet product SMC2 LAN9514_ETH 0xec00 USB/Ethernet /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet product SOHOWARE NUB110 0x9110 10/100 USB Ethernet /* SOLID YEAR products */ product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard /* SONY products */ product SONY DSC 0x0010 DSC cameras product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7 product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2 product SONY MSACUS1 0x002d Memorystick MSAC-US1 product SONY HANDYCAM 0x002e Handycam product SONY MSC 0x0032 MSC memory stick slot product SONY CLIE_35 0x0038 Sony Clie v3.5 product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick product SONY CLIE_40 0x0066 Sony Clie v4.0 product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03 product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot product SONY CLIE_S360 0x0095 Sony Clie s360 product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot product SONY CLIE_41 0x009a Sony Clie v4.1 product SONY CLIE_NX60 0x00da Sony Clie nx60 product SONY CLIE_TH55 0x0144 Sony Clie th55 product SONY CLIE_TJ37 0x0169 Sony Clie tj37 product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1 product SONY QN3 0x0437 Sony QN3 CMD-Jxx phone cable /* Sony Ericsson products */ product SONYERICSSON DCU10 0x0528 DCU-10 Phone Data Cable product SONYERICSSON DATAPILOT 0x2003 Datapilot Phone Cable /* SOURCENEXT products */ product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger /* SparkLAN products */ product SPARKLAN RT2573 0x0004 RT2573 product SPARKLAN RT2870_1 0x0006 RT2870 product SPARKLAN RT3070 0x0010 RT3070 /* Soundgraph products */ product SOUNDGRAPH IMON_VFD 0x0044 Antec Veris Elite VFD Panel, Knob, and Remote product SOUNDGRAPH SSTONE_LC16 0xffdc Silverstone LC16 VFD Panel, Knob, and Remote /* Speed Dragon Multimedia products */ product SPEEDDRAGON MS3303H 0x110b MS3303H Serial /* Sphairon Access Systems GmbH products */ product SPHAIRON UB801R 0x0110 UB801R /* Stelera Wireless products */ product STELERA ZEROCD 0x1000 Zerocd Installer product STELERA C105 0x1002 Stelera/Bandrish C105 USB product STELERA E1003 0x1003 3G modem product STELERA E1004 0x1004 3G modem product STELERA E1005 0x1005 3G modem product STELERA E1006 0x1006 3G modem product STELERA E1007 0x1007 3G modem product STELERA E1008 0x1008 3G modem product STELERA E1009 0x1009 3G modem product STELERA E100A 0x100a 3G modem product STELERA E100B 0x100b 3G modem product STELERA E100C 0x100c 3G modem product STELERA E100D 0x100d 3G modem product STELERA E100E 0x100e 3G modem product STELERA E100F 0x100f 3G modem product STELERA E1010 0x1010 3G modem product STELERA E1011 0x1011 3G modem product STELERA E1012 0x1012 3G modem /* STMicroelectronics products */ product STMICRO BIOCPU 0x2016 Biometric Coprocessor product STMICRO COMMUNICATOR 0x7554 USB Communicator product STMICRO ST72682 0xfada USB 2.0 Flash drive controller /* STSN products */ product STSN STSN0001 0x0001 Internet Access Device /* SUN Corporation products */ product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 product SUNTAC VS10U 0x0009 SUNTAC Slipper U product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 /* Sun Microsystems products */ product SUN KEYBOARD_TYPE_6 0x0005 Type 6 USB keyboard product SUN KEYBOARD_TYPE_7 0x00a2 Type 7 USB keyboard /* XXX The above is a North American PC style keyboard possibly */ product SUN MOUSE 0x0100 Type 6 USB mouse product SUN KBD_HUB 0x100e Kbd Hub /* Sunplus Innovation Technology Inc. products */ product SUNPLUS USBMOUSE 0x0007 USB Optical Mouse /* Super Top products */ product SUPERTOP IDE 0x6600 USB-IDE product SUPERTOP FLASHDRIVE 0x121c extrememory Snippy /* Syntech products */ product SYNTECH CPT8001C 0x0001 CPT-8001C Barcode scanner product SYNTECH CYPHERLAB100 0x1000 CipherLab USB Barcode Scanner /* Teclast products */ product TECLAST TLC300 0x3203 USB Media Player /* Testo products */ product TESTO USB_INTERFACE 0x0001 FTDI compatible adapter /* TexTech products */ product TEXTECH DUMMY 0x0000 Dummy product product TEXTECH U2M_1 0x0101 Textech USB MIDI cable product TEXTECH U2M_2 0x1806 Textech USB MIDI cable /* The Mobility Lab products */ product TML USB_SERIAL 0x0064 FTDI compatible adapter /* Thurlby Thandar Instrument products */ product TTI QL355P 0x03e8 FTDI compatible adapter /* Supra products */ product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem product DIAMOND2 RIO600USB 0x5001 Rio 600 USB product DIAMOND2 RIO800USB 0x5002 Rio 800 USB /* Surecom Technology products */ product SURECOM EP9001G2A 0x11f2 EP-9001-G rev 2A product SURECOM RT2570 0x11f3 RT2570 product SURECOM RT2573 0x31f3 RT2573 /* Sweex products */ product SWEEX ZD1211 0x1809 ZD1211 product SWEEX2 LW153 0x0153 LW153 product SWEEX2 LW154 0x0154 LW154 product SWEEX2 LW303 0x0302 LW303 product SWEEX2 LW313 0x0313 LW313 /* System TALKS, Inc. */ product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL /* Tapwave products */ product TAPWAVE ZODIAC 0x0100 Zodiac /* Taugagreining products */ product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) /* TCTMobile products */ product TCTMOBILE X060S 0x0000 X060S 3G modem product TCTMOBILE X080S 0xf000 X080S 3G modem /* TDK products */ product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 product TDK BT_DONGLE 0x0309 Bluetooth USB dongle /* TEAC products */ product TEAC FD05PUB 0x0000 FD-05PUB floppy /* Tekram Technology products */ product TEKRAM QUICKWLAN 0x1630 QuickWLAN product TEKRAM ZD1211_1 0x5630 ZD1211 product TEKRAM ZD1211_2 0x6630 ZD1211 /* Telex Communications products */ product TELEX MIC1 0x0001 Enhanced USB Microphone /* Telit products */ product TELIT UC864E 0x1003 UC864E 3G modem product TELIT UC864G 0x1004 UC864G 3G modem /* Ten X Technology, Inc. */ product TENX UAUDIO0 0xf211 USB audio headset /* Texas Intel products */ product TI UTUSB41 0x1446 UT-USB41 hub product TI TUSB2046 0x2046 TUSB2046 hub /* Thrustmaster products */ product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad /* TLayTech products */ product TLAYTECH TEU800 0x1682 TEU800 3G modem /* Topre Corporation products */ product TOPRE HHKB 0x0100 HHKB Professional /* Toshiba Corporation products */ product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 product TOSHIBA RT3070 0x0a07 RT3070 product TOSHIBA G450 0x0d45 G450 modem product TOSHIBA HSDPA 0x1302 G450 modem product TOSHIBA TRANSMEMORY 0x6545 USB ThumbDrive /* Trek Technology products */ product TREK THUMBDRIVE 0x1111 ThumbDrive product TREK MEMKEY 0x8888 IBM USB Memory Key product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB /* TRENDnet products */ product TRENDNET RTL8192CU 0x624d RTL8192CU product TRENDNET TEW646UBH 0x646b TEW-646UBH product TRENDNET RTL8188CU 0x648b RTL8188CU /* Tripp-Lite products */ product TRIPPLITE U209 0x2008 Serial /* Trumpion products */ product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller product TRUMPION C3310 0x1100 Comotron C3310 MP3 player product TRUMPION MP3 0x1200 MP3 player /* TwinMOS */ product TWINMOS G240 0xa006 G240 product TWINMOS MDIV 0x1325 Memory Disk IV /* Ubiquam products */ product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520) /* Ultima products */ product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner /* UMAX products */ product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner product UMAX ASTRA3400 0x0060 Astra 3400 Scanner /* U-MEDIA Communications products */ product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware) product UMEDIA TEW429UB_A 0x300a TEW-429UB_A product UMEDIA TEW429UB 0x300b TEW-429UB product UMEDIA TEW429UBC1 0x300d TEW-429UB C1 product UMEDIA RT2870_1 0x300e RT2870 product UMEDIA ALL0298V2 0x3204 ALL0298 v2 product UMEDIA AR5523_2 0x3205 AR5523 product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware) /* Universal Access products */ product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter /* Unknown products */ product UNKNOWN4 NF_RIC 0x0001 FTDI compatible adapter /* USI products */ product USI MC60 0x10c5 MC60 Serial /* U.S. Robotics products */ product USR USR5422 0x0118 USR5422 WLAN product USR USR5423 0x0121 USR5423 WLAN /* VIA Technologies products */ product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge /* VIA Labs */ product VIALABS USB30SATABRIDGE 0x0700 USB 3.0 SATA Bridge /* Vaisala products */ product VAISALA CABLE 0x0200 USB Interface cable /* Vertex products */ product VERTEX VW110L 0x0100 Vertex VW110L modem /* VidzMedia products */ product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H /* Vision products */ product VISION VC6452V002 0x0002 CPiA Camera /* Visioneer products */ product VISIONEER 7600 0x0211 OneTouch 7600 product VISIONEER 5300 0x0221 OneTouch 5300 product VISIONEER 3000 0x0224 Scanport 3000 product VISIONEER 6100 0x0231 OneTouch 6100 product VISIONEER 6200 0x0311 OneTouch 6200 product VISIONEER 8100 0x0321 OneTouch 8100 product VISIONEER 8600 0x0331 OneTouch 8600 /* Vivitar products */ product VIVITAR 35XX 0x0003 Vivicam 35Xx /* VTech products */ product VTECH RT2570 0x3012 RT2570 product VTECH ZD1211B 0x3014 ZD1211B /* Wacom products */ product WACOM CT0405U 0x0000 CT-0405-U Tablet product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet /* WAGO Kontakttechnik GmbH products */ product WAGO SERVICECABLE 0x07a6 USB Service Cable 750-923 /* WaveSense products */ product WAVESENSE JAZZ 0xaaaa Jazz blood glucose meter /* WCH products */ product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 DUMMY 0x0000 Dummy product product WCH2 CH341SER_2 0x5523 CH341/CH340 USB-Serial Bridge product WCH2 CH341SER 0x7523 CH341/CH340 USB-Serial Bridge product WCH2 U2M 0X752d CH345 USB2.0-MIDI /* West Mountain Radio products */ product WESTMOUNTAIN RIGBLASTER_ADVANTAGE 0x0003 RIGblaster Advantage /* Western Digital products */ product WESTERN COMBO 0x0200 Firewire USB Combo product WESTERN EXTHDD 0x0400 External HDD product WESTERN HUB 0x0500 USB HUB product WESTERN MYBOOK 0x0901 MyBook External HDD product WESTERN MYPASSPORT_00 0x0704 MyPassport External HDD product WESTERN MYPASSPORT_11 0x0741 MyPassport External HDD product WESTERN MYPASSPORT_01 0x0746 MyPassport External HDD product WESTERN MYPASSPORT_02 0x0748 MyPassport External HDD product WESTERN MYPASSPORT_03 0x074A MyPassport External HDD product WESTERN MYPASSPORT_04 0x074C MyPassport External HDD product WESTERN MYPASSPORT_05 0x074E MyPassport External HDD product WESTERN MYPASSPORT_06 0x07A6 MyPassport External HDD product WESTERN MYPASSPORT_07 0x07A8 MyPassport External HDD product WESTERN MYPASSPORT_08 0x07AA MyPassport External HDD product WESTERN MYPASSPORT_09 0x07AC MyPassport External HDD product WESTERN MYPASSPORT_10 0x07AE MyPassport External HDD product WESTERN MYPASSPORTES_00 0x070A MyPassport Essential External HDD product WESTERN MYPASSPORTES_01 0x071A MyPassport Essential External HDD product WESTERN MYPASSPORTES_02 0x0730 MyPassport Essential External HDD product WESTERN MYPASSPORTES_03 0x0732 MyPassport Essential External HDD product WESTERN MYPASSPORTES_04 0x0740 MyPassport Essential External HDD product WESTERN MYPASSPORTES_05 0x0742 MyPassport Essential External HDD product WESTERN MYPASSPORTES_06 0x0750 MyPassport Essential External HDD product WESTERN MYPASSPORTES_07 0x0752 MyPassport Essential External HDD product WESTERN MYPASSPORTES_08 0x07A0 MyPassport Essential External HDD product WESTERN MYPASSPORTES_09 0x07A2 MyPassport Essential External HDD /* WeTelecom products */ product WETELECOM WM_D200 0x6801 WM-D200 /* WIENER Plein & Baus GmbH products */ product WIENERPLEINBAUS PL512 0x0010 PL512 PSU product WIENERPLEINBAUS RCM 0x0011 RCM Remote Control product WIENERPLEINBAUS MPOD 0x0012 MPOD PSU product WIENERPLEINBAUS CML 0x0015 CML Data Logger /* Windbond Electronics */ product WINBOND UH104 0x5518 4-port USB Hub /* WinMaxGroup products */ product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C /* Wistron NeWeb products */ product WISTRONNEWEB WNC0600 0x0326 WNC-0600USB product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN product WISTRONNEWEB UR055G 0x0711 UR055G product WISTRONNEWEB O8494 0x0804 ORiNOCO 802.11n product WISTRONNEWEB AR5523_1 0x0826 AR5523 product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware) product WISTRONNEWEB AR5523_2 0x082a AR5523 product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware) /* Xerox products */ product XEROX WCM15 0xffef WorkCenter M15 /* Xirlink products */ product XIRLINK PCCAM 0x8080 IBM PC Camera /* Xyratex products */ product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* Yamaha products */ product YAMAHA UX256 0x1000 UX256 MIDI I/F product YAMAHA UX96 0x1008 UX96 MIDI I/F product YAMAHA RPU200 0x3104 RP-U200 product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router /* Yano products */ product YANO U640MO 0x0101 U640MO-03 product YANO FW800HD 0x05fc METALWEAR-HDD /* Y.C. Cable products */ product YCCABLE PL2303 0x0fba PL2303 Serial /* Y-E Data products */ product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U /* Yiso Wireless Co. products */ product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card /* Z-Com products */ product ZCOM M4Y750 0x0001 M4Y-750 product ZCOM XI725 0x0002 XI-725/726 product ZCOM XI735 0x0005 XI-735 product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN product ZCOM ZD1211 0x0011 ZD1211 product ZCOM AR5523 0x0012 AR5523 product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware) product ZCOM XM142 0x0015 XM-142 product ZCOM ZD1211B 0x001a ZD1211B product ZCOM RT2870_1 0x0022 RT2870 product ZCOM UB81 0x0023 UB81 product ZCOM RT2870_2 0x0025 RT2870 product ZCOM UB82 0x0026 UB82 /* Zinwell products */ product ZINWELL RT2570 0x0260 RT2570 product ZINWELL RT2870_1 0x0280 RT2870 product ZINWELL RT2870_2 0x0282 RT2870 product ZINWELL RT3072_1 0x0283 RT3072 product ZINWELL RT3072_2 0x0284 RT3072 product ZINWELL RT3070 0x5257 RT3070 /* Zoom Telephonics, Inc. products */ product ZOOM 2986L 0x9700 2986L Fax modem /* Zoran Microelectronics products */ product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC /* Zydas Technology Corporation products */ product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg product ZYDAS ZD1211B 0x1215 ZD1211B product ZYDAS ZD1221 0x1221 ZD1221 /* ZyXEL Communication Co. products */ product ZYXEL OMNI56K 0x1500 Omni 56K Plus product ZYXEL 980N 0x2011 Scorpion-980N keyboard product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 product ZYXEL G200V2 0x3407 G-200 v2 product ZYXEL AG225H 0x3409 AG-225H product ZYXEL M202 0x340a M-202 product ZYXEL G220V2 0x340f G-220 v2 product ZYXEL G202 0x3410 G-202 product ZYXEL RT2870_1 0x3416 RT2870 product ZYXEL NWD271N 0x3417 NWD-271N product ZYXEL NWD211AN 0x3418 NWD-211AN product ZYXEL RT2870_2 0x341a RT2870 product ZYXEL RT3070 0x341e NWD2105 product ZYXEL RTL8192CU 0x341f RTL8192CU product ZYXEL NWD2705 0x3421 NWD2705 Index: projects/clang380-import/sys/fs/nullfs/null_vnops.c =================================================================== --- projects/clang380-import/sys/fs/nullfs/null_vnops.c (revision 293015) +++ projects/clang380-import/sys/fs/nullfs/null_vnops.c (revision 293016) @@ -1,927 +1,929 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * John Heidemann of the UCLA Ficus project. * * 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)null_vnops.c 8.6 (Berkeley) 5/27/95 * * Ancestors: * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92 * ...and... * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project * * $FreeBSD$ */ /* * Null Layer * * (See mount_nullfs(8) for more information.) * * The null layer duplicates a portion of the filesystem * name space under a new name. In this respect, it is * similar to the loopback filesystem. It differs from * the loopback fs in two respects: it is implemented using * a stackable layers techniques, and its "null-node"s stack above * all lower-layer vnodes, not just over directory vnodes. * * The null layer has two purposes. First, it serves as a demonstration * of layering by proving a layer which does nothing. (It actually * does everything the loopback filesystem does, which is slightly * more than nothing.) Second, the null layer can serve as a prototype * layer. Since it provides all necessary layer framework, * new filesystem layers can be created very easily be starting * with a null layer. * * The remainder of this man page examines the null layer as a basis * for constructing new layers. * * * INSTANTIATING NEW NULL LAYERS * * New null layers are created with mount_nullfs(8). * Mount_nullfs(8) takes two arguments, the pathname * of the lower vfs (target-pn) and the pathname where the null * layer will appear in the namespace (alias-pn). After * the null layer is put into place, the contents * of target-pn subtree will be aliased under alias-pn. * * * OPERATION OF A NULL LAYER * * The null layer is the minimum filesystem layer, * simply bypassing all possible operations to the lower layer * for processing there. The majority of its activity centers * on the bypass routine, through which nearly all vnode operations * pass. * * The bypass routine accepts arbitrary vnode operations for * handling by the lower layer. It begins by examing vnode * operation arguments and replacing any null-nodes by their * lower-layer equivlants. It then invokes the operation * on the lower layer. Finally, it replaces the null-nodes * in the arguments and, if a vnode is return by the operation, * stacks a null-node on top of the returned vnode. * * Although bypass handles most operations, vop_getattr, vop_lock, * vop_unlock, vop_inactive, vop_reclaim, and vop_print are not * bypassed. Vop_getattr must change the fsid being returned. * Vop_lock and vop_unlock must handle any locking for the * current vnode as well as pass the lock request down. * Vop_inactive and vop_reclaim are not bypassed so that * they can handle freeing null-layer specific data. Vop_print * is not bypassed to avoid excessive debugging information. * Also, certain vnode operations change the locking state within * the operation (create, mknod, remove, link, rename, mkdir, rmdir, * and symlink). Ideally these operations should not change the * lock state, but should be changed to let the caller of the * function unlock them. Otherwise all intermediate vnode layers * (such as union, umapfs, etc) must catch these functions to do * the necessary locking at their layer. * * * INSTANTIATING VNODE STACKS * * Mounting associates the null layer with a lower layer, * effect stacking two VFSes. Vnode stacks are instead * created on demand as files are accessed. * * The initial mount creates a single vnode stack for the * root of the new null layer. All other vnode stacks * are created as a result of vnode operations on * this or other null vnode stacks. * * New vnode stacks come into existance as a result of * an operation which returns a vnode. * The bypass routine stacks a null-node above the new * vnode before returning it to the caller. * * For example, imagine mounting a null layer with * "mount_nullfs /usr/include /dev/layer/null". * Changing directory to /dev/layer/null will assign * the root null-node (which was created when the null layer was mounted). * Now consider opening "sys". A vop_lookup would be * done on the root null-node. This operation would bypass through * to the lower layer which would return a vnode representing * the UFS "sys". Null_bypass then builds a null-node * aliasing the UFS "sys" and returns this to the caller. * Later operations on the null-node "sys" will repeat this * process when constructing other vnode stacks. * * * CREATING OTHER FILE SYSTEM LAYERS * * One of the easiest ways to construct new filesystem layers is to make * a copy of the null layer, rename all files and variables, and * then begin modifing the copy. Sed can be used to easily rename * all variables. * * The umap layer is an example of a layer descended from the * null layer. * * * INVOKING OPERATIONS ON LOWER LAYERS * * There are two techniques to invoke operations on a lower layer * when the operation cannot be completely bypassed. Each method * is appropriate in different situations. In both cases, * it is the responsibility of the aliasing layer to make * the operation arguments "correct" for the lower layer * by mapping a vnode arguments to the lower layer. * * The first approach is to call the aliasing layer's bypass routine. * This method is most suitable when you wish to invoke the operation * currently being handled on the lower layer. It has the advantage * that the bypass routine already must do argument mapping. * An example of this is null_getattrs in the null layer. * * A second approach is to directly invoke vnode operations on * the lower layer with the VOP_OPERATIONNAME interface. * The advantage of this method is that it is easy to invoke * arbitrary operations on the lower layer. The disadvantage * is that vnode arguments must be manualy mapped. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ SYSCTL_INT(_debug, OID_AUTO, nullfs_bug_bypass, CTLFLAG_RW, &null_bug_bypass, 0, ""); /* * This is the 10-Apr-92 bypass routine. * This version has been optimized for speed, throwing away some * safety checks. It should still always work, but it's not as * robust to programmer errors. * * In general, we map all vnodes going down and unmap them on the way back. * As an exception to this, vnodes can be marked "unmapped" by setting * the Nth bit in operation's vdesc_flags. * * Also, some BSD vnode operations have the side effect of vrele'ing * their arguments. With stacking, the reference counts are held * by the upper node, not the lower one, so we must handle these * side-effects here. This is not of concern in Sun-derived systems * since there are no such side-effects. * * This makes the following assumptions: * - only one returned vpp * - no INOUT vpp's (Sun's vop_open has one of these) * - the vnode operation vector of the first vnode should be used * to determine what implementation of the op should be invoked * - all mapped vnodes are of our vnode-type (NEEDSWORK: * problems on rmdir'ing mount points and renaming?) */ int null_bypass(struct vop_generic_args *ap) { struct vnode **this_vp_p; int error; struct vnode *old_vps[VDESC_MAX_VPS]; struct vnode **vps_p[VDESC_MAX_VPS]; struct vnode ***vppp; struct vnodeop_desc *descp = ap->a_desc; int reles, i; if (null_bug_bypass) printf ("null_bypass: %s\n", descp->vdesc_name); #ifdef DIAGNOSTIC /* * We require at least one vp. */ if (descp->vdesc_vp_offsets == NULL || descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) panic ("null_bypass: no vp's in map"); #endif /* * Map the vnodes going in. * Later, we'll invoke the operation based on * the first mapped vnode's operation vector. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ vps_p[i] = this_vp_p = VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap); /* * We're not guaranteed that any but the first vnode * are of our type. Check for and don't map any * that aren't. (We must always map first vp or vclean fails.) */ if (i && (*this_vp_p == NULLVP || (*this_vp_p)->v_op != &null_vnodeops)) { old_vps[i] = NULLVP; } else { old_vps[i] = *this_vp_p; *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p); /* * XXX - Several operations have the side effect * of vrele'ing their vp's. We must account for * that. (This should go away in the future.) */ if (reles & VDESC_VP0_WILLRELE) VREF(*this_vp_p); } } /* * Call the operation on the lower layer * with the modified argument structure. */ if (vps_p[0] && *vps_p[0]) error = VCALL(ap); else { printf("null_bypass: no map for %s\n", descp->vdesc_name); error = EINVAL; } /* * Maintain the illusion of call-by-value * by restoring vnodes in the argument structure * to their original value. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ if (old_vps[i]) { *(vps_p[i]) = old_vps[i]; #if 0 if (reles & VDESC_VP0_WILLUNLOCK) VOP_UNLOCK(*(vps_p[i]), 0); #endif if (reles & VDESC_VP0_WILLRELE) vrele(*(vps_p[i])); } } /* * Map the possible out-going vpp * (Assumes that the lower layer always returns * a VREF'ed vpp unless it gets an error.) */ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !(descp->vdesc_flags & VDESC_NOMAP_VPP) && !error) { /* * XXX - even though some ops have vpp returned vp's, * several ops actually vrele this before returning. * We must avoid these ops. * (This should go away when these ops are regularized.) */ if (descp->vdesc_flags & VDESC_VPP_WILLRELE) goto out; vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset,ap); if (*vppp) error = null_nodeget(old_vps[0]->v_mount, **vppp, *vppp); } out: return (error); } static int null_add_writecount(struct vop_add_writecount_args *ap) { struct vnode *lvp, *vp; int error; vp = ap->a_vp; lvp = NULLVPTOLOWERVP(vp); KASSERT(vp->v_writecount + ap->a_inc >= 0, ("wrong writecount inc")); if (vp->v_writecount > 0 && vp->v_writecount + ap->a_inc == 0) error = VOP_ADD_WRITECOUNT(lvp, -1); else if (vp->v_writecount == 0 && vp->v_writecount + ap->a_inc > 0) error = VOP_ADD_WRITECOUNT(lvp, 1); else error = 0; if (error == 0) vp->v_writecount += ap->a_inc; return (error); } /* * We have to carry on the locking protocol on the null layer vnodes * as we progress through the tree. We also have to enforce read-only * if this layer is mounted read-only. */ static int null_lookup(struct vop_lookup_args *ap) { struct componentname *cnp = ap->a_cnp; struct vnode *dvp = ap->a_dvp; int flags = cnp->cn_flags; struct vnode *vp, *ldvp, *lvp; struct mount *mp; int error; mp = dvp->v_mount; if ((flags & ISLASTCN) != 0 && (mp->mnt_flag & MNT_RDONLY) != 0 && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* * Although it is possible to call null_bypass(), we'll do * a direct call to reduce overhead */ ldvp = NULLVPTOLOWERVP(dvp); vp = lvp = NULL; KASSERT((ldvp->v_vflag & VV_ROOT) == 0 || ((dvp->v_vflag & VV_ROOT) != 0 && (flags & ISDOTDOT) == 0), ("ldvp %p fl %#x dvp %p fl %#x flags %#x", ldvp, ldvp->v_vflag, dvp, dvp->v_vflag, flags)); /* * Hold ldvp. The reference on it, owned by dvp, is lost in * case of dvp reclamation, and we need ldvp to move our lock * from ldvp to dvp. */ vhold(ldvp); error = VOP_LOOKUP(ldvp, &lvp, cnp); /* * VOP_LOOKUP() on lower vnode may unlock ldvp, which allows * dvp to be reclaimed due to shared v_vnlock. Check for the * doomed state and return error. */ if ((error == 0 || error == EJUSTRETURN) && (dvp->v_iflag & VI_DOOMED) != 0) { error = ENOENT; if (lvp != NULL) vput(lvp); /* * If vgone() did reclaimed dvp before curthread * relocked ldvp, the locks of dvp and ldpv are no * longer shared. In this case, relock of ldvp in * lower fs VOP_LOOKUP() does not restore the locking * state of dvp. Compensate for this by unlocking * ldvp and locking dvp, which is also correct if the * locks are still shared. */ VOP_UNLOCK(ldvp, 0); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); } vdrop(ldvp); if (error == EJUSTRETURN && (flags & ISLASTCN) != 0 && (mp->mnt_flag & MNT_RDONLY) != 0 && (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) error = EROFS; if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) { if (ldvp == lvp) { *ap->a_vpp = dvp; VREF(dvp); vrele(lvp); } else { error = null_nodeget(mp, lvp, &vp); if (error == 0) *ap->a_vpp = vp; } } return (error); } static int null_open(struct vop_open_args *ap) { int retval; struct vnode *vp, *ldvp; vp = ap->a_vp; ldvp = NULLVPTOLOWERVP(vp); retval = null_bypass(&ap->a_gen); if (retval == 0) vp->v_object = ldvp->v_object; return (retval); } /* * Setattr call. Disallow write attempts if the layer is mounted read-only. */ static int null_setattr(struct vop_setattr_args *ap) { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && (vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VCHR: case VBLK: case VSOCK: case VFIFO: if (vap->va_flags != VNOVAL) return (EOPNOTSUPP); return (0); case VREG: case VLNK: default: /* * Disallow write attempts if the filesystem is * mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); } } return (null_bypass((struct vop_generic_args *)ap)); } /* * We handle getattr only to change the fsid. */ static int null_getattr(struct vop_getattr_args *ap) { int error; if ((error = null_bypass((struct vop_generic_args *)ap)) != 0) return (error); ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } /* * Handle to disallow write access if mounted read-only. */ static int null_access(struct vop_access_args *ap) { struct vnode *vp = ap->a_vp; accmode_t accmode = ap->a_accmode; /* * Disallow write attempts on read-only layers; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ if (accmode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } } return (null_bypass((struct vop_generic_args *)ap)); } static int null_accessx(struct vop_accessx_args *ap) { struct vnode *vp = ap->a_vp; accmode_t accmode = ap->a_accmode; /* * Disallow write attempts on read-only layers; * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ if (accmode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } } return (null_bypass((struct vop_generic_args *)ap)); } /* * Increasing refcount of lower vnode is needed at least for the case * when lower FS is NFS to do sillyrename if the file is in use. * Unfortunately v_usecount is incremented in many places in * the kernel and, as such, there may be races that result in * the NFS client doing an extraneous silly rename, but that seems * preferable to not doing a silly rename when it is needed. */ static int null_remove(struct vop_remove_args *ap) { int retval, vreleit; - struct vnode *lvp; + struct vnode *lvp, *vp; - if (vrefcnt(ap->a_vp) > 1) { - lvp = NULLVPTOLOWERVP(ap->a_vp); + vp = ap->a_vp; + if (vrefcnt(vp) > 1) { + lvp = NULLVPTOLOWERVP(vp); VREF(lvp); vreleit = 1; } else vreleit = 0; + VTONULL(vp)->null_flags |= NULLV_DROP; retval = null_bypass(&ap->a_gen); if (vreleit != 0) vrele(lvp); return (retval); } /* * We handle this to eliminate null FS to lower FS * file moving. Don't know why we don't allow this, * possibly we should. */ static int null_rename(struct vop_rename_args *ap) { struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct vnode *tvp = ap->a_tvp; struct null_node *tnn; /* Check for cross-device rename. */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); return (EXDEV); } if (tvp != NULL) { tnn = VTONULL(tvp); tnn->null_flags |= NULLV_DROP; } return (null_bypass((struct vop_generic_args *)ap)); } /* * We need to process our own vnode lock and then clear the * interlock flag as it applies only to our vnode, not the * vnodes below us on the stack. */ static int null_lock(struct vop_lock1_args *ap) { struct vnode *vp = ap->a_vp; int flags = ap->a_flags; struct null_node *nn; struct vnode *lvp; int error; if ((flags & LK_INTERLOCK) == 0) { VI_LOCK(vp); ap->a_flags = flags |= LK_INTERLOCK; } nn = VTONULL(vp); /* * If we're still active we must ask the lower layer to * lock as ffs has special lock considerations in it's * vop lock. */ if (nn != NULL && (lvp = NULLVPTOLOWERVP(vp)) != NULL) { VI_LOCK_FLAGS(lvp, MTX_DUPOK); VI_UNLOCK(vp); /* * We have to hold the vnode here to solve a potential * reclaim race. If we're forcibly vgone'd while we * still have refs, a thread could be sleeping inside * the lowervp's vop_lock routine. When we vgone we will * drop our last ref to the lowervp, which would allow it * to be reclaimed. The lowervp could then be recycled, * in which case it is not legal to be sleeping in it's VOP. * We prevent it from being recycled by holding the vnode * here. */ vholdl(lvp); error = VOP_LOCK(lvp, flags); /* * We might have slept to get the lock and someone might have * clean our vnode already, switching vnode lock from one in * lowervp to v_lock in our own vnode structure. Handle this * case by reacquiring correct lock in requested mode. */ if (VTONULL(vp) == NULL && error == 0) { ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK); switch (flags & LK_TYPE_MASK) { case LK_SHARED: ap->a_flags |= LK_SHARED; break; case LK_UPGRADE: case LK_EXCLUSIVE: ap->a_flags |= LK_EXCLUSIVE; break; default: panic("Unsupported lock request %d\n", ap->a_flags); } VOP_UNLOCK(lvp, 0); error = vop_stdlock(ap); } vdrop(lvp); } else error = vop_stdlock(ap); return (error); } /* * We need to process our own vnode unlock and then clear the * interlock flag as it applies only to our vnode, not the * vnodes below us on the stack. */ static int null_unlock(struct vop_unlock_args *ap) { struct vnode *vp = ap->a_vp; int flags = ap->a_flags; int mtxlkflag = 0; struct null_node *nn; struct vnode *lvp; int error; if ((flags & LK_INTERLOCK) != 0) mtxlkflag = 1; else if (mtx_owned(VI_MTX(vp)) == 0) { VI_LOCK(vp); mtxlkflag = 2; } nn = VTONULL(vp); if (nn != NULL && (lvp = NULLVPTOLOWERVP(vp)) != NULL) { VI_LOCK_FLAGS(lvp, MTX_DUPOK); flags |= LK_INTERLOCK; vholdl(lvp); VI_UNLOCK(vp); error = VOP_UNLOCK(lvp, flags); vdrop(lvp); if (mtxlkflag == 0) VI_LOCK(vp); } else { if (mtxlkflag == 2) VI_UNLOCK(vp); error = vop_stdunlock(ap); } return (error); } /* * Do not allow the VOP_INACTIVE to be passed to the lower layer, * since the reference count on the lower vnode is not related to * ours. */ static int null_inactive(struct vop_inactive_args *ap __unused) { struct vnode *vp, *lvp; struct null_node *xp; struct mount *mp; struct null_mount *xmp; vp = ap->a_vp; xp = VTONULL(vp); lvp = NULLVPTOLOWERVP(vp); mp = vp->v_mount; xmp = MOUNTTONULLMOUNT(mp); if ((xmp->nullm_flags & NULLM_CACHE) == 0 || (xp->null_flags & NULLV_DROP) != 0 || (lvp->v_vflag & VV_NOSYNC) != 0) { /* * If this is the last reference and caching of the * nullfs vnodes is not enabled, or the lower vnode is * deleted, then free up the vnode so as not to tie up * the lower vnodes. */ vp->v_object = NULL; vrecycle(vp); } return (0); } /* * Now, the nullfs vnode and, due to the sharing lock, the lower * vnode, are exclusively locked, and we shall destroy the null vnode. */ static int null_reclaim(struct vop_reclaim_args *ap) { struct vnode *vp; struct null_node *xp; struct vnode *lowervp; vp = ap->a_vp; xp = VTONULL(vp); lowervp = xp->null_lowervp; KASSERT(lowervp != NULL && vp->v_vnlock != &vp->v_lock, ("Reclaiming incomplete null vnode %p", vp)); null_hashrem(xp); /* * Use the interlock to protect the clearing of v_data to * prevent faults in null_lock(). */ lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL); VI_LOCK(vp); vp->v_data = NULL; vp->v_object = NULL; vp->v_vnlock = &vp->v_lock; VI_UNLOCK(vp); /* * If we were opened for write, we leased one write reference * to the lower vnode. If this is a reclamation due to the * forced unmount, undo the reference now. */ if (vp->v_writecount > 0) VOP_ADD_WRITECOUNT(lowervp, -1); if ((xp->null_flags & NULLV_NOUNLOCK) != 0) vunref(lowervp); else vput(lowervp); free(xp, M_NULLFSNODE); return (0); } static int null_print(struct vop_print_args *ap) { struct vnode *vp = ap->a_vp; printf("\tvp=%p, lowervp=%p\n", vp, VTONULL(vp)->null_lowervp); return (0); } /* ARGSUSED */ static int null_getwritemount(struct vop_getwritemount_args *ap) { struct null_node *xp; struct vnode *lowervp; struct vnode *vp; vp = ap->a_vp; VI_LOCK(vp); xp = VTONULL(vp); if (xp && (lowervp = xp->null_lowervp)) { VI_LOCK_FLAGS(lowervp, MTX_DUPOK); VI_UNLOCK(vp); vholdl(lowervp); VI_UNLOCK(lowervp); VOP_GETWRITEMOUNT(lowervp, ap->a_mpp); vdrop(lowervp); } else { VI_UNLOCK(vp); *(ap->a_mpp) = NULL; } return (0); } static int null_vptofh(struct vop_vptofh_args *ap) { struct vnode *lvp; lvp = NULLVPTOLOWERVP(ap->a_vp); return VOP_VPTOFH(lvp, ap->a_fhp); } static int null_vptocnp(struct vop_vptocnp_args *ap) { struct vnode *vp = ap->a_vp; struct vnode **dvp = ap->a_vpp; struct vnode *lvp, *ldvp; struct ucred *cred = ap->a_cred; int error, locked; if (vp->v_type == VDIR) return (vop_stdvptocnp(ap)); locked = VOP_ISLOCKED(vp); lvp = NULLVPTOLOWERVP(vp); vhold(lvp); VOP_UNLOCK(vp, 0); /* vp is held by vn_vptocnp_locked that called us */ ldvp = lvp; vref(lvp); error = vn_vptocnp(&ldvp, cred, ap->a_buf, ap->a_buflen); vdrop(lvp); if (error != 0) { vn_lock(vp, locked | LK_RETRY); return (ENOENT); } /* * Exclusive lock is required by insmntque1 call in * null_nodeget() */ error = vn_lock(ldvp, LK_EXCLUSIVE); if (error != 0) { vrele(ldvp); vn_lock(vp, locked | LK_RETRY); return (ENOENT); } vref(ldvp); error = null_nodeget(vp->v_mount, ldvp, dvp); if (error == 0) { #ifdef DIAGNOSTIC NULLVPTOLOWERVP(*dvp); #endif VOP_UNLOCK(*dvp, 0); /* keep reference on *dvp */ } vn_lock(vp, locked | LK_RETRY); return (error); } /* * Global vfs data structures */ struct vop_vector null_vnodeops = { .vop_bypass = null_bypass, .vop_access = null_access, .vop_accessx = null_accessx, .vop_advlockpurge = vop_stdadvlockpurge, .vop_bmap = VOP_EOPNOTSUPP, .vop_getattr = null_getattr, .vop_getwritemount = null_getwritemount, .vop_inactive = null_inactive, .vop_islocked = vop_stdislocked, .vop_lock1 = null_lock, .vop_lookup = null_lookup, .vop_open = null_open, .vop_print = null_print, .vop_reclaim = null_reclaim, .vop_remove = null_remove, .vop_rename = null_rename, .vop_setattr = null_setattr, .vop_strategy = VOP_EOPNOTSUPP, .vop_unlock = null_unlock, .vop_vptocnp = null_vptocnp, .vop_vptofh = null_vptofh, .vop_add_writecount = null_add_writecount, }; Index: projects/clang380-import/sys/modules/Makefile =================================================================== --- projects/clang380-import/sys/modules/Makefile (revision 293015) +++ projects/clang380-import/sys/modules/Makefile (revision 293016) @@ -1,784 +1,786 @@ # $FreeBSD$ SYSDIR?=${.CURDIR}/.. .include "${SYSDIR}/conf/kern.opts.mk" SUBDIR_PARALLEL= # Modules that include binary-only blobs of microcode should be selectable by # MK_SOURCELESS_UCODE option (see below). .if defined(MODULES_OVERRIDE) && !defined(ALL_MODULES) SUBDIR=${MODULES_OVERRIDE} .else SUBDIR= \ ${_3dfx} \ ${_3dfx_linux} \ ${_aac} \ ${_aacraid} \ accf_data \ accf_dns \ accf_http \ acl_nfs4 \ acl_posix1e \ ${_acpi} \ ae \ ${_aesni} \ age \ ${_agp} \ aha \ ${_ahb} \ ahci \ ${_aic} \ aic7xxx \ aio \ alc \ ale \ alq \ ${_amdsbwd} \ ${_amdtemp} \ amr \ ${_an} \ ${_aout} \ ${_apm} \ ${_arcmsr} \ ${_arcnet} \ ${_asmc} \ ata \ ath \ ath_pci \ ${_autofs} \ ${_auxio} \ ${_bce} \ bfe \ bge \ ${_bxe} \ ${_bios} \ ${_bktr} \ ${_bm} \ bridgestp \ bwi \ bwn \ cam \ ${_canbepm} \ ${_canbus} \ ${_cardbus} \ ${_carp} \ cas \ ${_cbb} \ cc \ cd9660 \ cd9660_iconv \ ${_ce} \ ${_cfi} \ ${_ciss} \ cloudabi \ ${_cloudabi64} \ ${_cm} \ ${_cmx} \ ${_coff} \ ${_coretemp} \ ${_cp} \ ${_cpsw} \ ${_cpuctl} \ ${_cpufreq} \ ${_crypto} \ ${_cryptodev} \ ${_cs} \ ${_ct} \ ${_ctau} \ ctl \ ${_cxgb} \ ${_cxgbe} \ dc \ dcons \ dcons_crom \ de \ ${_dpms} \ ${_dpt} \ ${_drm} \ ${_drm2} \ dummynet \ ${_ed} \ ${_elink} \ ${_em} \ en \ ${_ep} \ ${_epic} \ esp \ ${_et} \ ${_ex} \ ${_exca} \ ext2fs \ ${_fatm} \ fdc \ fdescfs \ ${_fe} \ ${_filemon} \ firewire \ firmware \ fuse \ ${_fxp} \ gem \ geom \ ${_glxiic} \ ${_glxsb} \ hatm \ hifn \ hme \ ${_hpt27xx} \ ${_hptiop} \ ${_hptmv} \ ${_hptnr} \ ${_hptrr} \ hwpmc \ ${_hyperv} \ i2c \ ${_ibcore} \ ${_ibcs2} \ ${_ichwd} \ ${_ida} \ ${_ie} \ if_bridge \ if_disc \ if_edsc \ ${_if_enc} \ if_epair \ ${_if_gif} \ ${_if_gre} \ ${_if_me} \ if_lagg \ ${_if_ndis} \ ${_if_stf} \ if_tap \ if_tun \ if_vlan \ if_vxlan \ ${_igb} \ ${_iir} \ imgact_binmisc \ ${_io} \ ${_ioat} \ ${_ipoib} \ ${_ipdivert} \ ${_ipfilter} \ ${_ipfw} \ ipfw_nat \ ${_ipmi} \ ip6_mroute_mod \ ip_mroute_mod \ ${_ips} \ ${_ipw} \ ${_ipwfw} \ ${_isci} \ isp \ ${_ispfw} \ ${_iwi} \ ${_iwifw} \ ${_iwm} \ ${_iwmfw} \ ${_iwn} \ ${_iwnfw} \ ${_ix} \ ${_ixv} \ ${_ixgb} \ ${_ixl} \ ${_ixlv} \ jme \ joy \ kbdmux \ kgssapi \ kgssapi_krb5 \ khelp \ krpc \ ksyms \ le \ lge \ libalias \ libiconv \ libmbpool \ libmchain \ ${_linprocfs} \ ${_linsysfs} \ ${_linux} \ ${_linux_common} \ ${_linux64} \ linuxkpi \ lmc \ lpt \ mac_biba \ mac_bsdextended \ mac_ifoff \ mac_lomac \ mac_mls \ mac_none \ mac_partition \ mac_portacl \ mac_seeotheruids \ mac_stub \ mac_test \ malo \ mcd \ md \ mdio \ mem \ mfi \ mii \ mlx \ ${_mlx4} \ ${_mlx4ib} \ ${_mlxen} \ ${_mlx5} \ ${_mlx5en} \ ${_mly} \ mmc \ mmcsd \ mpr \ mps \ mpt \ mqueue \ mrsas \ msdosfs \ msdosfs_iconv \ ${_mse} \ msk \ ${_mthca} \ mvs \ mwl \ ${_mwlfw} \ mxge \ my \ ${_nandfs} \ ${_nandsim} \ ${_ncr} \ ${_ncv} \ ${_ndis} \ netfpga10g \ ${_netgraph} \ ${_nfe} \ nfscl \ nfscommon \ nfsd \ nfslock \ nfslockd \ nfssvc \ nge \ nmdm \ ${_nsp} \ nullfs \ ${_ntb} \ ${_nvd} \ ${_nvme} \ ${_nvram} \ ${_nxge} \ oce \ otus \ otusfw \ ow \ ${_padlock} \ ${_padlock_rng} \ patm \ ${_pccard} \ ${_pcfclock} \ pcn \ ${_pf} \ ${_pflog} \ ${_pfsync} \ plip \ ${_pmc} \ ${_pms} \ ppbus \ ppc \ ppi \ pps \ procfs \ proto \ pseudofs \ ${_pst} \ pty \ puc \ ${_qlxge} \ ${_qlxgb} \ ${_qlxgbe} \ ral \ ${_ralfw} \ ${_random_fortuna} \ ${_random_yarrow} \ ${_random_other} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ re \ reiserfs \ rl \ + rtwn \ + rtwnfw \ ${_s3} \ ${_safe} \ ${_sbni} \ scc \ scd \ ${_scsi_low} \ sdhci \ sdhci_pci \ sem \ send \ ${_sf} \ ${_sfxge} \ sge \ ${_si} \ siba_bwn \ siftr \ siis \ sis \ sk \ smbfs \ sn \ ${_snc} \ snp \ sound \ ${_speaker} \ ${_splash} \ ${_sppp} \ ste \ ${_stg} \ stge \ ${_streams} \ ${_svr4} \ ${_sym} \ ${_syscons} \ sysvipc \ ${_ti} \ tcp/fastpath \ tests/framework \ tests/callout_test \ tl \ tmpfs \ ${_toecore} \ ${_tpm} \ trm \ ${_twa} \ twe \ tws \ tx \ ${_txp} \ uart \ ubsec \ udf \ udf_iconv \ ufs \ unionfs \ usb \ utopia \ ${_vesa} \ ${_virtio} \ vge \ ${_viawd} \ videomode \ vkbd \ ${_vmm} \ ${_vmware} \ ${_vpo} \ vr \ vte \ vx \ ${_vxge} \ wb \ ${_wbwd} \ ${_wds} \ ${_wi} \ ${_wl} \ wlan \ wlan_acl \ wlan_amrr \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ ${_wpi} \ ${_wpifw} \ ${_x86bios} \ ${_xe} \ xl \ zlib .if ${MK_AUTOFS} != "no" || defined(ALL_MODULES) _autofs= autofs .endif .if ${MK_CDDL} != "no" || defined(ALL_MODULES) .if (${MACHINE_CPUARCH} != "arm" || ${MACHINE_ARCH:Marmv6*} != "") && \ ${MACHINE_CPUARCH} != "mips" && \ ${MACHINE_CPUARCH} != "sparc64" SUBDIR+= dtrace .endif SUBDIR+= opensolaris .endif .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) .if exists(${.CURDIR}/../opencrypto) _crypto= crypto _cryptodev= cryptodev _random_fortuna=random_fortuna _random_yarrow= random_yarrow _random_other= random_other .endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) SUBDIR+= cuse .endif .if (${MK_INET_SUPPORT} != "no" || ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _carp= carp _toecore= toecore _if_enc= if_enc _if_gif= if_gif _if_gre= if_gre .endif .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _if_stf= if_stf .endif .if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES) _if_me= if_me _ipdivert= ipdivert _ipfw= ipfw .endif .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES) _ipfilter= ipfilter .endif .if ${MK_ISCSI} != "no" || defined(ALL_MODULES) SUBDIR+= iscsi SUBDIR+= iscsi_initiator .endif .if ${MK_NAND} != "no" || defined(ALL_MODULES) _nandfs= nandfs _nandsim= nandsim .endif .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif .if (${MK_PF} != "no" && (${MK_INET_SUPPORT} != "no" || \ ${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES) _pf= pf _pflog= pflog .if ${MK_INET_SUPPORT} != "no" _pfsync= pfsync .endif .endif .if ${MK_SOURCELESS_UCODE} != "no" _bce= bce _fatm= fatm _fxp= fxp _ispfw= ispfw _mwlfw= mwlfw _ralfw= ralfw _sf= sf _ti= ti _txp= txp .endif .if ${MK_SOURCELESS_UCODE} != "no" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_ARCH:C/mips(el)?/mips/} != "mips" && \ ${MACHINE_ARCH} != "powerpc" _cxgbe= cxgbe .endif .if ${MK_ZFS} != "no" || defined(ALL_MODULES) SUBDIR+= zfs .endif .if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "arm" && \ ${MACHINE_CPUARCH} != "mips" && ${MACHINE_CPUARCH} != "powerpc" _syscons= syscons _vpo= vpo .endif .if ${MACHINE_CPUARCH} != "mips" # no BUS_SPACE_UNSPECIFIED # No barrier instruction support (specific to this driver) _sym= sym # intr_disable() is a macro, causes problems .if ${MK_SOURCELESS_UCODE} != "no" _cxgb= cxgb .endif .endif .if ${MACHINE_CPUARCH} == "aarch64" _em= em _igb= igb .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _agp= agp _an= an _aout= aout _bktr= bktr _bxe= bxe _cardbus= cardbus _cbb= cbb _cpuctl= cpuctl _cpufreq= cpufreq _cs= cs _dpms= dpms _drm= drm _drm2= drm2 _ed= ed _em= em _ep= ep _et= et _exca= exca _fe= fe _filemon= filemon .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ibcore= ibcore .endif _if_ndis= if_ndis _igb= igb _io= io .if ${MK_OFED} != "no" || defined(ALL_MODULES) _ipoib= ipoib .endif _ix= ix _ixv= ixv _linprocfs= linprocfs _linsysfs= linsysfs _linux= linux _ndis= ndis _pccard= pccard .if ${MK_OFED} != "no" || defined(ALL_MODULES) _rdma= rdma .endif _safe= safe _scsi_low= scsi_low _si= si _speaker= speaker _splash= splash _sppp= sppp _vmware= vmware _vxge= vxge _wbwd= wbwd _wi= wi _xe= xe .if ${MACHINE} != "pc98" _aac= aac _aacraid= aacraid _acpi= acpi .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _aesni= aesni .endif _amdsbwd= amdsbwd _amdtemp= amdtemp _arcmsr= arcmsr _asmc= asmc _ciss= ciss _cmx= cmx _coretemp= coretemp .if ${MK_SOURCELESS_HOST} != "no" _hpt27xx= hpt27xx .endif _hptiop= hptiop .if ${MK_SOURCELESS_HOST} != "no" _hptmv= hptmv _hptnr= hptnr _hptrr= hptrr .endif _hyperv= hyperv _ichwd= ichwd _ida= ida _iir= iir _ipmi= ipmi _ips= ips _isci= isci _ipw= ipw _iwi= iwi _iwm= iwm _iwn= iwn _ixgb= ixgb .if ${MK_SOURCELESS_UCODE} != "no" _ipwfw= ipwfw _iwifw= iwifw _iwmfw= iwmfw _iwnfw= iwnfw .endif .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mlx4= mlx4 _mlx4ib= mlx4ib _mlxen= mlxen .endif _mlx5= mlx5 .if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \ defined(ALL_MODULES) _mlx5en= mlx5en .endif _mly= mly .if ${MK_OFED} != "no" || defined(ALL_MODULES) _mthca= mthca .endif _nfe= nfe _nvd= nvd _nvme= nvme _nvram= nvram _nxge= nxge .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock _padlock_rng= padlock_rng _rdrand_rng= rdrand_rng .endif _s3= s3 _tpm= tpm _twa= twa _vesa= vesa _viawd= viawd _virtio= virtio _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" _wpifw= wpifw .endif _x86bios= x86bios .endif .endif .if ${MACHINE_CPUARCH} == "amd64" _ioat= ioat _ixl= ixl _ixlv= ixlv _linux64= linux64 _linux_common= linux_common _ntb= ntb _pms= pms _qlxge= qlxge _qlxgb= qlxgb _qlxgbe= qlxgbe _sfxge= sfxge .if ${MK_BHYVE} != "no" || defined(ALL_MODULES) _vmm= vmm .endif .endif .if ${MACHINE_CPUARCH} == "i386" # XXX some of these can move to the general case when de-i386'ed # XXX some of these can move now, but are untested on other architectures. _3dfx= 3dfx _3dfx_linux= 3dfx_linux _aic= aic _apm= apm _arcnet= arcnet .if ${MK_SOURCELESS_UCODE} != "no" _ce= ce .endif _coff= coff .if ${MK_SOURCELESS_UCODE} != "no" _cp= cp .endif _elink= elink _glxiic= glxiic _glxsb= glxsb #_ibcs2= ibcs2 _ie= ie _mse= mse _ncr= ncr _ncv= ncv _nsp= nsp _pcfclock= pcfclock _pst= pst _sbni= sbni _streams= streams _stg= stg _svr4= svr4 _wds= wds .if ${MACHINE} == "i386" .if ${MK_EISA} != "no" _ahb= ahb .endif _bios= bios _cm= cm .if ${MK_SOURCELESS_UCODE} != "no" _ctau= ctau .endif _dpt= dpt _ex= ex _wl= wl .elif ${MACHINE} == "pc98" _canbepm= canbepm _canbus= canbus _ct= ct _pmc= pmc _snc= snc .endif .endif .if ${MACHINE_CPUARCH} == "arm" _cfi= cfi _cpsw= cpsw .endif .if ${MACHINE_CPUARCH} == "powerpc" _agp= agp _an= an _bm= bm _cardbus= cardbus _cbb= cbb _cfi= cfi _cpufreq= cpufreq _drm= drm _exca= exca _nvram= powermac_nvram _pccard= pccard _wi= wi .endif .if ${MACHINE_ARCH} == "powerpc64" _drm2= drm2 .endif .if ${MACHINE_CPUARCH} == "sparc64" _auxio= auxio _em= em _epic= epic _igb= igb .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" _cloudabi64= cloudabi64 .endif .endif SUBDIR+=${MODULES_EXTRA} .for reject in ${WITHOUT_MODULES} SUBDIR:= ${SUBDIR:N${reject}} .endfor # Calling kldxref(8) for each module is expensive. .if !defined(NO_XREF) .MAKEFLAGS+= -DNO_XREF afterinstall: @if type kldxref >/dev/null 2>&1; then \ ${ECHO} kldxref ${DESTDIR}${KMODDIR}; \ kldxref ${DESTDIR}${KMODDIR}; \ fi .endif .include "${SYSDIR}/conf/config.mk" # Use sys/conf/kmod.mk's MPATH to avoid redundantly running in every subdir. .if !defined(__MPATH) __MPATH!=find ${SYSDIR:tA}/ -name \*_if.m .export __MPATH .endif SUBDIR:= ${SUBDIR:u:O} .include Index: projects/clang380-import/sys/modules/rtwnfw/Makefile =================================================================== --- projects/clang380-import/sys/modules/rtwnfw/Makefile (nonexistent) +++ projects/clang380-import/sys/modules/rtwnfw/Makefile (revision 293016) @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= rtwnrtl8192cU rtwnrtl8192cUB + +.include Property changes on: projects/clang380-import/sys/modules/rtwnfw/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/modules/rtwnfw/Makefile.inc =================================================================== --- projects/clang380-import/sys/modules/rtwnfw/Makefile.inc (nonexistent) +++ projects/clang380-import/sys/modules/rtwnfw/Makefile.inc (revision 293016) @@ -0,0 +1,15 @@ +# $FreeBSD$ +# +# Common rules for building firmware. Note this gets auto-included +# by the subdir Makefile's as a consequence of included bsd.kmod.mk. + +_FIRM= ${IMG}.fw + +CLEANFILES+= ${_FIRM} + +FIRMWS= ${_FIRM}:${KMOD}:111 + +FIRMWARE_LICENSE= realtek + +${_FIRM}: ${.CURDIR}/../../../contrib/dev/rtwn/${_FIRM}.uu + uudecode -p $? > ${.TARGET} Property changes on: projects/clang380-import/sys/modules/rtwnfw/Makefile.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cU/Makefile =================================================================== --- projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cU/Makefile (nonexistent) +++ projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cU/Makefile (revision 293016) @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= rtwn-rtl8192cfwU +IMG= rtwn-rtl8192cfwU + +.include Property changes on: projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cU/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cUB/Makefile =================================================================== --- projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cUB/Makefile (nonexistent) +++ projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cUB/Makefile (revision 293016) @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= rtwn-rtl8192cfwU_B +IMG= rtwn-rtl8192cfwU_B + +.include Property changes on: projects/clang380-import/sys/modules/rtwnfw/rtwnrtl8192cUB/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/net/bpf.c =================================================================== --- projects/clang380-import/sys/net/bpf.c (revision 293015) +++ projects/clang380-import/sys/net/bpf.c (revision 293016) @@ -1,2947 +1,2961 @@ /*- * Copyright (c) 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)bpf.c 8.4 (Berkeley) 1/9/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_bpf.h" #include "opt_compat.h" #include "opt_netgraph.h" #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 #include #ifdef BPF_JITTER #include #endif #include #include +#include #include #include #include #include #include #include #include MALLOC_DEFINE(M_BPF, "BPF", "BPF data"); struct bpf_if { #define bif_next bif_ext.bif_next #define bif_dlist bif_ext.bif_dlist struct bpf_if_ext bif_ext; /* public members */ u_int bif_dlt; /* link layer type */ u_int bif_hdrlen; /* length of link header */ struct ifnet *bif_ifp; /* corresponding interface */ struct rwlock bif_lock; /* interface lock */ LIST_HEAD(, bpf_d) bif_wlist; /* writer-only list */ int bif_flags; /* Interface flags */ }; CTASSERT(offsetof(struct bpf_if, bif_ext) == 0); #if defined(DEV_BPF) || defined(NETGRAPH_BPF) #define PRINET 26 /* interruptible */ #define SIZEOF_BPF_HDR(type) \ (offsetof(type, bh_hdrlen) + sizeof(((type *)0)->bh_hdrlen)) #ifdef COMPAT_FREEBSD32 #include #include #define BPF_ALIGNMENT32 sizeof(int32_t) #define BPF_WORDALIGN32(x) (((x)+(BPF_ALIGNMENT32-1))&~(BPF_ALIGNMENT32-1)) #ifndef BURN_BRIDGES /* * 32-bit version of structure prepended to each packet. We use this header * instead of the standard one for 32-bit streams. We mark the a stream as * 32-bit the first time we see a 32-bit compat ioctl request. */ struct bpf_hdr32 { struct timeval32 bh_tstamp; /* time stamp */ uint32_t bh_caplen; /* length of captured portion */ uint32_t bh_datalen; /* original length of packet */ uint16_t bh_hdrlen; /* length of bpf header (this struct plus alignment padding) */ }; #endif struct bpf_program32 { u_int bf_len; uint32_t bf_insns; }; struct bpf_dltlist32 { u_int bfl_len; u_int bfl_list; }; #define BIOCSETF32 _IOW('B', 103, struct bpf_program32) #define BIOCSRTIMEOUT32 _IOW('B', 109, struct timeval32) #define BIOCGRTIMEOUT32 _IOR('B', 110, struct timeval32) #define BIOCGDLTLIST32 _IOWR('B', 121, struct bpf_dltlist32) #define BIOCSETWF32 _IOW('B', 123, struct bpf_program32) #define BIOCSETFNR32 _IOW('B', 130, struct bpf_program32) #endif /* * bpf_iflist is a list of BPF interface structures, each corresponding to a * specific DLT. The same network interface might have several BPF interface * structures registered by different layers in the stack (i.e., 802.11 * frames, ethernet frames, etc). */ static LIST_HEAD(, bpf_if) bpf_iflist, bpf_freelist; static struct mtx bpf_mtx; /* bpf global lock */ static int bpf_bpfd_cnt; static void bpf_attachd(struct bpf_d *, struct bpf_if *); static void bpf_detachd(struct bpf_d *); static void bpf_detachd_locked(struct bpf_d *); static void bpf_freed(struct bpf_d *); static int bpf_movein(struct uio *, int, struct ifnet *, struct mbuf **, - struct sockaddr *, int *, struct bpf_insn *); + struct sockaddr *, int *, struct bpf_d *); static int bpf_setif(struct bpf_d *, struct ifreq *); static void bpf_timed_out(void *); static __inline void bpf_wakeup(struct bpf_d *); static void catchpacket(struct bpf_d *, u_char *, u_int, u_int, void (*)(struct bpf_d *, caddr_t, u_int, void *, u_int), struct bintime *); static void reset_d(struct bpf_d *); static int bpf_setf(struct bpf_d *, struct bpf_program *, u_long cmd); static int bpf_getdltlist(struct bpf_d *, struct bpf_dltlist *); static int bpf_setdlt(struct bpf_d *, u_int); static void filt_bpfdetach(struct knote *); static int filt_bpfread(struct knote *, long); static void bpf_drvinit(void *); static int bpf_stats_sysctl(SYSCTL_HANDLER_ARGS); SYSCTL_NODE(_net, OID_AUTO, bpf, CTLFLAG_RW, 0, "bpf sysctl"); int bpf_maxinsns = BPF_MAXINSNS; SYSCTL_INT(_net_bpf, OID_AUTO, maxinsns, CTLFLAG_RW, &bpf_maxinsns, 0, "Maximum bpf program instructions"); static int bpf_zerocopy_enable = 0; SYSCTL_INT(_net_bpf, OID_AUTO, zerocopy_enable, CTLFLAG_RW, &bpf_zerocopy_enable, 0, "Enable new zero-copy BPF buffer sessions"); static SYSCTL_NODE(_net_bpf, OID_AUTO, stats, CTLFLAG_MPSAFE | CTLFLAG_RW, bpf_stats_sysctl, "bpf statistics portal"); static VNET_DEFINE(int, bpf_optimize_writers) = 0; #define V_bpf_optimize_writers VNET(bpf_optimize_writers) SYSCTL_INT(_net_bpf, OID_AUTO, optimize_writers, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(bpf_optimize_writers), 0, "Do not send packets until BPF program is set"); static d_open_t bpfopen; static d_read_t bpfread; static d_write_t bpfwrite; static d_ioctl_t bpfioctl; static d_poll_t bpfpoll; static d_kqfilter_t bpfkqfilter; static struct cdevsw bpf_cdevsw = { .d_version = D_VERSION, .d_open = bpfopen, .d_read = bpfread, .d_write = bpfwrite, .d_ioctl = bpfioctl, .d_poll = bpfpoll, .d_name = "bpf", .d_kqfilter = bpfkqfilter, }; static struct filterops bpfread_filtops = { .f_isfd = 1, .f_detach = filt_bpfdetach, .f_event = filt_bpfread, }; eventhandler_tag bpf_ifdetach_cookie = NULL; /* * LOCKING MODEL USED BY BPF: * Locks: * 1) global lock (BPF_LOCK). Mutex, used to protect interface addition/removal, * some global counters and every bpf_if reference. * 2) Interface lock. Rwlock, used to protect list of BPF descriptors and their filters. * 3) Descriptor lock. Mutex, used to protect BPF buffers and various structure fields * used by bpf_mtap code. * * Lock order: * * Global lock, interface lock, descriptor lock * * We have to acquire interface lock before descriptor main lock due to BPF_MTAP[2] * working model. In many places (like bpf_detachd) we start with BPF descriptor * (and we need to at least rlock it to get reliable interface pointer). This * gives us potential LOR. As a result, we use global lock to protect from bpf_if * change in every such place. * * Changing d->bd_bif is protected by 1) global lock, 2) interface lock and * 3) descriptor main wlock. * Reading bd_bif can be protected by any of these locks, typically global lock. * * Changing read/write BPF filter is protected by the same three locks, * the same applies for reading. * * Sleeping in global lock is not allowed due to bpfdetach() using it. */ /* * Wrapper functions for various buffering methods. If the set of buffer * modes expands, we will probably want to introduce a switch data structure * similar to protosw, et. */ static void bpf_append_bytes(struct bpf_d *d, caddr_t buf, u_int offset, void *src, u_int len) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_BUFFER: return (bpf_buffer_append_bytes(d, buf, offset, src, len)); case BPF_BUFMODE_ZBUF: d->bd_zcopy++; return (bpf_zerocopy_append_bytes(d, buf, offset, src, len)); default: panic("bpf_buf_append_bytes"); } } static void bpf_append_mbuf(struct bpf_d *d, caddr_t buf, u_int offset, void *src, u_int len) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_BUFFER: return (bpf_buffer_append_mbuf(d, buf, offset, src, len)); case BPF_BUFMODE_ZBUF: d->bd_zcopy++; return (bpf_zerocopy_append_mbuf(d, buf, offset, src, len)); default: panic("bpf_buf_append_mbuf"); } } /* * This function gets called when the free buffer is re-assigned. */ static void bpf_buf_reclaimed(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_BUFFER: return; case BPF_BUFMODE_ZBUF: bpf_zerocopy_buf_reclaimed(d); return; default: panic("bpf_buf_reclaimed"); } } /* * If the buffer mechanism has a way to decide that a held buffer can be made * free, then it is exposed via the bpf_canfreebuf() interface. (1) is * returned if the buffer can be discarded, (0) is returned if it cannot. */ static int bpf_canfreebuf(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_ZBUF: return (bpf_zerocopy_canfreebuf(d)); } return (0); } /* * Allow the buffer model to indicate that the current store buffer is * immutable, regardless of the appearance of space. Return (1) if the * buffer is writable, and (0) if not. */ static int bpf_canwritebuf(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_ZBUF: return (bpf_zerocopy_canwritebuf(d)); } return (1); } /* * Notify buffer model that an attempt to write to the store buffer has * resulted in a dropped packet, in which case the buffer may be considered * full. */ static void bpf_buffull(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_ZBUF: bpf_zerocopy_buffull(d); break; } } /* * Notify the buffer model that a buffer has moved into the hold position. */ void bpf_bufheld(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); switch (d->bd_bufmode) { case BPF_BUFMODE_ZBUF: bpf_zerocopy_bufheld(d); break; } } static void bpf_free(struct bpf_d *d) { switch (d->bd_bufmode) { case BPF_BUFMODE_BUFFER: return (bpf_buffer_free(d)); case BPF_BUFMODE_ZBUF: return (bpf_zerocopy_free(d)); default: panic("bpf_buf_free"); } } static int bpf_uiomove(struct bpf_d *d, caddr_t buf, u_int len, struct uio *uio) { if (d->bd_bufmode != BPF_BUFMODE_BUFFER) return (EOPNOTSUPP); return (bpf_buffer_uiomove(d, buf, len, uio)); } static int bpf_ioctl_sblen(struct bpf_d *d, u_int *i) { if (d->bd_bufmode != BPF_BUFMODE_BUFFER) return (EOPNOTSUPP); return (bpf_buffer_ioctl_sblen(d, i)); } static int bpf_ioctl_getzmax(struct thread *td, struct bpf_d *d, size_t *i) { if (d->bd_bufmode != BPF_BUFMODE_ZBUF) return (EOPNOTSUPP); return (bpf_zerocopy_ioctl_getzmax(td, d, i)); } static int bpf_ioctl_rotzbuf(struct thread *td, struct bpf_d *d, struct bpf_zbuf *bz) { if (d->bd_bufmode != BPF_BUFMODE_ZBUF) return (EOPNOTSUPP); return (bpf_zerocopy_ioctl_rotzbuf(td, d, bz)); } static int bpf_ioctl_setzbuf(struct thread *td, struct bpf_d *d, struct bpf_zbuf *bz) { if (d->bd_bufmode != BPF_BUFMODE_ZBUF) return (EOPNOTSUPP); return (bpf_zerocopy_ioctl_setzbuf(td, d, bz)); } /* * General BPF functions. */ static int bpf_movein(struct uio *uio, int linktype, struct ifnet *ifp, struct mbuf **mp, - struct sockaddr *sockp, int *hdrlen, struct bpf_insn *wfilter) + struct sockaddr *sockp, int *hdrlen, struct bpf_d *d) { const struct ieee80211_bpf_params *p; struct ether_header *eh; struct mbuf *m; int error; int len; int hlen; int slen; /* * Build a sockaddr based on the data link layer type. * We do this at this level because the ethernet header * is copied directly into the data field of the sockaddr. * In the case of SLIP, there is no header and the packet * is forwarded as is. * Also, we are careful to leave room at the front of the mbuf * for the link level header. */ switch (linktype) { case DLT_SLIP: sockp->sa_family = AF_INET; hlen = 0; break; case DLT_EN10MB: sockp->sa_family = AF_UNSPEC; /* XXX Would MAXLINKHDR be better? */ hlen = ETHER_HDR_LEN; break; case DLT_FDDI: sockp->sa_family = AF_IMPLINK; hlen = 0; break; case DLT_RAW: sockp->sa_family = AF_UNSPEC; hlen = 0; break; case DLT_NULL: /* * null interface types require a 4 byte pseudo header which * corresponds to the address family of the packet. */ sockp->sa_family = AF_UNSPEC; hlen = 4; break; case DLT_ATM_RFC1483: /* * en atm driver requires 4-byte atm pseudo header. * though it isn't standard, vpi:vci needs to be * specified anyway. */ sockp->sa_family = AF_UNSPEC; hlen = 12; /* XXX 4(ATM_PH) + 3(LLC) + 5(SNAP) */ break; case DLT_PPP: sockp->sa_family = AF_UNSPEC; hlen = 4; /* This should match PPP_HDRLEN */ break; case DLT_IEEE802_11: /* IEEE 802.11 wireless */ sockp->sa_family = AF_IEEE80211; hlen = 0; break; case DLT_IEEE802_11_RADIO: /* IEEE 802.11 wireless w/ phy params */ sockp->sa_family = AF_IEEE80211; sockp->sa_len = 12; /* XXX != 0 */ hlen = sizeof(struct ieee80211_bpf_params); break; default: return (EIO); } len = uio->uio_resid; if (len < hlen || len - hlen > ifp->if_mtu) return (EMSGSIZE); m = m_get2(len, M_WAITOK, MT_DATA, M_PKTHDR); if (m == NULL) return (EIO); m->m_pkthdr.len = m->m_len = len; *mp = m; error = uiomove(mtod(m, u_char *), len, uio); if (error) goto bad; - slen = bpf_filter(wfilter, mtod(m, u_char *), len, len); + slen = bpf_filter(d->bd_wfilter, mtod(m, u_char *), len, len); if (slen == 0) { error = EPERM; goto bad; } /* Check for multicast destination */ switch (linktype) { case DLT_EN10MB: eh = mtod(m, struct ether_header *); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (bcmp(ifp->if_broadcastaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; } + if (d->bd_hdrcmplt == 0) { + memcpy(eh->ether_shost, IF_LLADDR(ifp), + sizeof(eh->ether_shost)); + } break; } /* * Make room for link header, and copy it to sockaddr */ if (hlen != 0) { if (sockp->sa_family == AF_IEEE80211) { /* * Collect true length from the parameter header * NB: sockp is known to be zero'd so if we do a * short copy unspecified parameters will be * zero. * NB: packet may not be aligned after stripping * bpf params * XXX check ibp_vers */ p = mtod(m, const struct ieee80211_bpf_params *); hlen = p->ibp_len; if (hlen > sizeof(sockp->sa_data)) { error = EINVAL; goto bad; } } bcopy(mtod(m, const void *), sockp->sa_data, hlen); } *hdrlen = hlen; return (0); bad: m_freem(m); return (error); } /* * Attach file to the bpf interface, i.e. make d listen on bp. */ static void bpf_attachd(struct bpf_d *d, struct bpf_if *bp) { int op_w; BPF_LOCK_ASSERT(); /* * Save sysctl value to protect from sysctl change * between reads */ op_w = V_bpf_optimize_writers || d->bd_writer; if (d->bd_bif != NULL) bpf_detachd_locked(d); /* * Point d at bp, and add d to the interface's list. * Since there are many applications using BPF for * sending raw packets only (dhcpd, cdpd are good examples) * we can delay adding d to the list of active listeners until * some filter is configured. */ BPFIF_WLOCK(bp); BPFD_LOCK(d); d->bd_bif = bp; if (op_w != 0) { /* Add to writers-only list */ LIST_INSERT_HEAD(&bp->bif_wlist, d, bd_next); /* * We decrement bd_writer on every filter set operation. * First BIOCSETF is done by pcap_open_live() to set up * snap length. After that appliation usually sets its own filter */ d->bd_writer = 2; } else LIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next); BPFD_UNLOCK(d); BPFIF_WUNLOCK(bp); bpf_bpfd_cnt++; CTR3(KTR_NET, "%s: bpf_attach called by pid %d, adding to %s list", __func__, d->bd_pid, d->bd_writer ? "writer" : "active"); if (op_w == 0) EVENTHANDLER_INVOKE(bpf_track, bp->bif_ifp, bp->bif_dlt, 1); } /* * Check if we need to upgrade our descriptor @d from write-only mode. */ static int bpf_check_upgrade(u_long cmd, struct bpf_d *d, struct bpf_insn *fcode, int flen) { int is_snap, need_upgrade; /* * Check if we've already upgraded or new filter is empty. */ if (d->bd_writer == 0 || fcode == NULL) return (0); need_upgrade = 0; /* * Check if cmd looks like snaplen setting from * pcap_bpf.c:pcap_open_live(). * Note we're not checking .k value here: * while pcap_open_live() definitely sets to to non-zero value, * we'd prefer to treat k=0 (deny ALL) case the same way: e.g. * do not consider upgrading immediately */ if (cmd == BIOCSETF && flen == 1 && fcode[0].code == (BPF_RET | BPF_K)) is_snap = 1; else is_snap = 0; if (is_snap == 0) { /* * We're setting first filter and it doesn't look like * setting snaplen. We're probably using bpf directly. * Upgrade immediately. */ need_upgrade = 1; } else { /* * Do not require upgrade by first BIOCSETF * (used to set snaplen) by pcap_open_live(). */ if (--d->bd_writer == 0) { /* * First snaplen filter has already * been set. This is probably catch-all * filter */ need_upgrade = 1; } } CTR5(KTR_NET, "%s: filter function set by pid %d, " "bd_writer counter %d, snap %d upgrade %d", __func__, d->bd_pid, d->bd_writer, is_snap, need_upgrade); return (need_upgrade); } /* * Add d to the list of active bp filters. * Requires bpf_attachd() to be called before. */ static void bpf_upgraded(struct bpf_d *d) { struct bpf_if *bp; BPF_LOCK_ASSERT(); bp = d->bd_bif; /* * Filter can be set several times without specifying interface. * Mark d as reader and exit. */ if (bp == NULL) { BPFD_LOCK(d); d->bd_writer = 0; BPFD_UNLOCK(d); return; } BPFIF_WLOCK(bp); BPFD_LOCK(d); /* Remove from writers-only list */ LIST_REMOVE(d, bd_next); LIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next); /* Mark d as reader */ d->bd_writer = 0; BPFD_UNLOCK(d); BPFIF_WUNLOCK(bp); CTR2(KTR_NET, "%s: upgrade required by pid %d", __func__, d->bd_pid); EVENTHANDLER_INVOKE(bpf_track, bp->bif_ifp, bp->bif_dlt, 1); } /* * Detach a file from its interface. */ static void bpf_detachd(struct bpf_d *d) { BPF_LOCK(); bpf_detachd_locked(d); BPF_UNLOCK(); } static void bpf_detachd_locked(struct bpf_d *d) { int error; struct bpf_if *bp; struct ifnet *ifp; CTR2(KTR_NET, "%s: detach required by pid %d", __func__, d->bd_pid); BPF_LOCK_ASSERT(); /* Check if descriptor is attached */ if ((bp = d->bd_bif) == NULL) return; BPFIF_WLOCK(bp); BPFD_LOCK(d); /* Save bd_writer value */ error = d->bd_writer; /* * Remove d from the interface's descriptor list. */ LIST_REMOVE(d, bd_next); ifp = bp->bif_ifp; d->bd_bif = NULL; BPFD_UNLOCK(d); BPFIF_WUNLOCK(bp); bpf_bpfd_cnt--; /* Call event handler iff d is attached */ if (error == 0) EVENTHANDLER_INVOKE(bpf_track, ifp, bp->bif_dlt, 0); /* * Check if this descriptor had requested promiscuous mode. * If so, turn it off. */ if (d->bd_promisc) { d->bd_promisc = 0; CURVNET_SET(ifp->if_vnet); error = ifpromisc(ifp, 0); CURVNET_RESTORE(); if (error != 0 && error != ENXIO) { /* * ENXIO can happen if a pccard is unplugged * Something is really wrong if we were able to put * the driver into promiscuous mode, but can't * take it out. */ if_printf(bp->bif_ifp, "bpf_detach: ifpromisc failed (%d)\n", error); } } } /* * Close the descriptor by detaching it from its interface, * deallocating its buffers, and marking it free. */ static void bpf_dtor(void *data) { struct bpf_d *d = data; BPFD_LOCK(d); if (d->bd_state == BPF_WAITING) callout_stop(&d->bd_callout); d->bd_state = BPF_IDLE; BPFD_UNLOCK(d); funsetown(&d->bd_sigio); bpf_detachd(d); #ifdef MAC mac_bpfdesc_destroy(d); #endif /* MAC */ seldrain(&d->bd_sel); knlist_destroy(&d->bd_sel.si_note); callout_drain(&d->bd_callout); bpf_freed(d); free(d, M_BPF); } /* * Open ethernet device. Returns ENXIO for illegal minor device number, * EBUSY if file is open by another process. */ /* ARGSUSED */ static int bpfopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct bpf_d *d; int error; d = malloc(sizeof(*d), M_BPF, M_WAITOK | M_ZERO); error = devfs_set_cdevpriv(d, bpf_dtor); if (error != 0) { free(d, M_BPF); return (error); } /* * For historical reasons, perform a one-time initialization call to * the buffer routines, even though we're not yet committed to a * particular buffer method. */ bpf_buffer_init(d); if ((flags & FREAD) == 0) d->bd_writer = 2; d->bd_hbuf_in_use = 0; d->bd_bufmode = BPF_BUFMODE_BUFFER; d->bd_sig = SIGIO; d->bd_direction = BPF_D_INOUT; BPF_PID_REFRESH(d, td); #ifdef MAC mac_bpfdesc_init(d); mac_bpfdesc_create(td->td_ucred, d); #endif mtx_init(&d->bd_lock, devtoname(dev), "bpf cdev lock", MTX_DEF); callout_init_mtx(&d->bd_callout, &d->bd_lock, 0); knlist_init_mtx(&d->bd_sel.si_note, &d->bd_lock); return (0); } /* * bpfread - read next chunk of packets from buffers */ static int bpfread(struct cdev *dev, struct uio *uio, int ioflag) { struct bpf_d *d; int error; int non_block; int timed_out; error = devfs_get_cdevpriv((void **)&d); if (error != 0) return (error); /* * Restrict application to use a buffer the same size as * as kernel buffers. */ if (uio->uio_resid != d->bd_bufsize) return (EINVAL); non_block = ((ioflag & O_NONBLOCK) != 0); BPFD_LOCK(d); BPF_PID_REFRESH_CUR(d); if (d->bd_bufmode != BPF_BUFMODE_BUFFER) { BPFD_UNLOCK(d); return (EOPNOTSUPP); } if (d->bd_state == BPF_WAITING) callout_stop(&d->bd_callout); timed_out = (d->bd_state == BPF_TIMED_OUT); d->bd_state = BPF_IDLE; while (d->bd_hbuf_in_use) { error = mtx_sleep(&d->bd_hbuf_in_use, &d->bd_lock, PRINET|PCATCH, "bd_hbuf", 0); if (error != 0) { BPFD_UNLOCK(d); return (error); } } /* * If the hold buffer is empty, then do a timed sleep, which * ends when the timeout expires or when enough packets * have arrived to fill the store buffer. */ while (d->bd_hbuf == NULL) { if (d->bd_slen != 0) { /* * A packet(s) either arrived since the previous * read or arrived while we were asleep. */ if (d->bd_immediate || non_block || timed_out) { /* * Rotate the buffers and return what's here * if we are in immediate mode, non-blocking * flag is set, or this descriptor timed out. */ ROTATE_BUFFERS(d); break; } } /* * No data is available, check to see if the bpf device * is still pointed at a real interface. If not, return * ENXIO so that the userland process knows to rebind * it before using it again. */ if (d->bd_bif == NULL) { BPFD_UNLOCK(d); return (ENXIO); } if (non_block) { BPFD_UNLOCK(d); return (EWOULDBLOCK); } error = msleep(d, &d->bd_lock, PRINET|PCATCH, "bpf", d->bd_rtout); if (error == EINTR || error == ERESTART) { BPFD_UNLOCK(d); return (error); } if (error == EWOULDBLOCK) { /* * On a timeout, return what's in the buffer, * which may be nothing. If there is something * in the store buffer, we can rotate the buffers. */ if (d->bd_hbuf) /* * We filled up the buffer in between * getting the timeout and arriving * here, so we don't need to rotate. */ break; if (d->bd_slen == 0) { BPFD_UNLOCK(d); return (0); } ROTATE_BUFFERS(d); break; } } /* * At this point, we know we have something in the hold slot. */ d->bd_hbuf_in_use = 1; BPFD_UNLOCK(d); /* * Move data from hold buffer into user space. * We know the entire buffer is transferred since * we checked above that the read buffer is bpf_bufsize bytes. * * We do not have to worry about simultaneous reads because * we waited for sole access to the hold buffer above. */ error = bpf_uiomove(d, d->bd_hbuf, d->bd_hlen, uio); BPFD_LOCK(d); KASSERT(d->bd_hbuf != NULL, ("bpfread: lost bd_hbuf")); d->bd_fbuf = d->bd_hbuf; d->bd_hbuf = NULL; d->bd_hlen = 0; bpf_buf_reclaimed(d); d->bd_hbuf_in_use = 0; wakeup(&d->bd_hbuf_in_use); BPFD_UNLOCK(d); return (error); } /* * If there are processes sleeping on this descriptor, wake them up. */ static __inline void bpf_wakeup(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); if (d->bd_state == BPF_WAITING) { callout_stop(&d->bd_callout); d->bd_state = BPF_IDLE; } wakeup(d); if (d->bd_async && d->bd_sig && d->bd_sigio) pgsigio(&d->bd_sigio, d->bd_sig, 0); selwakeuppri(&d->bd_sel, PRINET); KNOTE_LOCKED(&d->bd_sel.si_note, 0); } static void bpf_timed_out(void *arg) { struct bpf_d *d = (struct bpf_d *)arg; BPFD_LOCK_ASSERT(d); if (callout_pending(&d->bd_callout) || !callout_active(&d->bd_callout)) return; if (d->bd_state == BPF_WAITING) { d->bd_state = BPF_TIMED_OUT; if (d->bd_slen != 0) bpf_wakeup(d); } } static int bpf_ready(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); if (!bpf_canfreebuf(d) && d->bd_hlen != 0) return (1); if ((d->bd_immediate || d->bd_state == BPF_TIMED_OUT) && d->bd_slen != 0) return (1); return (0); } static int bpfwrite(struct cdev *dev, struct uio *uio, int ioflag) { struct bpf_d *d; struct ifnet *ifp; struct mbuf *m, *mc; struct sockaddr dst; + struct route ro; int error, hlen; error = devfs_get_cdevpriv((void **)&d); if (error != 0) return (error); BPF_PID_REFRESH_CUR(d); d->bd_wcount++; /* XXX: locking required */ if (d->bd_bif == NULL) { d->bd_wdcount++; return (ENXIO); } ifp = d->bd_bif->bif_ifp; if ((ifp->if_flags & IFF_UP) == 0) { d->bd_wdcount++; return (ENETDOWN); } if (uio->uio_resid == 0) { d->bd_wdcount++; return (0); } bzero(&dst, sizeof(dst)); m = NULL; hlen = 0; /* XXX: bpf_movein() can sleep */ error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, ifp, - &m, &dst, &hlen, d->bd_wfilter); + &m, &dst, &hlen, d); if (error) { d->bd_wdcount++; return (error); } d->bd_wfcount++; if (d->bd_hdrcmplt) dst.sa_family = pseudo_AF_HDRCMPLT; if (d->bd_feedback) { mc = m_dup(m, M_NOWAIT); if (mc != NULL) mc->m_pkthdr.rcvif = ifp; /* Set M_PROMISC for outgoing packets to be discarded. */ if (d->bd_direction == BPF_D_INOUT) m->m_flags |= M_PROMISC; } else mc = NULL; m->m_pkthdr.len -= hlen; m->m_len -= hlen; m->m_data += hlen; /* XXX */ CURVNET_SET(ifp->if_vnet); #ifdef MAC BPFD_LOCK(d); mac_bpfdesc_create_mbuf(d, m); if (mc != NULL) mac_bpfdesc_create_mbuf(d, mc); BPFD_UNLOCK(d); #endif - error = (*ifp->if_output)(ifp, m, &dst, NULL); + bzero(&ro, sizeof(ro)); + if (hlen != 0) { + ro.ro_prepend = (u_char *)&dst.sa_data; + ro.ro_plen = hlen; + ro.ro_flags = RT_HAS_HEADER; + } + + error = (*ifp->if_output)(ifp, m, &dst, &ro); if (error) d->bd_wdcount++; if (mc != NULL) { if (error == 0) (*ifp->if_input)(ifp, mc); else m_freem(mc); } CURVNET_RESTORE(); return (error); } /* * Reset a descriptor by flushing its packet buffer and clearing the receive * and drop counts. This is doable for kernel-only buffers, but with * zero-copy buffers, we can't write to (or rotate) buffers that are * currently owned by userspace. It would be nice if we could encapsulate * this logic in the buffer code rather than here. */ static void reset_d(struct bpf_d *d) { BPFD_LOCK_ASSERT(d); while (d->bd_hbuf_in_use) mtx_sleep(&d->bd_hbuf_in_use, &d->bd_lock, PRINET, "bd_hbuf", 0); if ((d->bd_hbuf != NULL) && (d->bd_bufmode != BPF_BUFMODE_ZBUF || bpf_canfreebuf(d))) { /* Free the hold buffer. */ d->bd_fbuf = d->bd_hbuf; d->bd_hbuf = NULL; d->bd_hlen = 0; bpf_buf_reclaimed(d); } if (bpf_canwritebuf(d)) d->bd_slen = 0; d->bd_rcount = 0; d->bd_dcount = 0; d->bd_fcount = 0; d->bd_wcount = 0; d->bd_wfcount = 0; d->bd_wdcount = 0; d->bd_zcopy = 0; } /* * FIONREAD Check for read packet available. * BIOCGBLEN Get buffer len [for read()]. * BIOCSETF Set read filter. * BIOCSETFNR Set read filter without resetting descriptor. * BIOCSETWF Set write filter. * BIOCFLUSH Flush read packet buffer. * BIOCPROMISC Put interface into promiscuous mode. * BIOCGDLT Get link layer type. * BIOCGETIF Get interface name. * BIOCSETIF Set interface. * BIOCSRTIMEOUT Set read timeout. * BIOCGRTIMEOUT Get read timeout. * BIOCGSTATS Get packet stats. * BIOCIMMEDIATE Set immediate mode. * BIOCVERSION Get filter language version. * BIOCGHDRCMPLT Get "header already complete" flag * BIOCSHDRCMPLT Set "header already complete" flag * BIOCGDIRECTION Get packet direction flag * BIOCSDIRECTION Set packet direction flag * BIOCGTSTAMP Get time stamp format and resolution. * BIOCSTSTAMP Set time stamp format and resolution. * BIOCLOCK Set "locked" flag * BIOCFEEDBACK Set packet feedback mode. * BIOCSETZBUF Set current zero-copy buffer locations. * BIOCGETZMAX Get maximum zero-copy buffer size. * BIOCROTZBUF Force rotation of zero-copy buffer * BIOCSETBUFMODE Set buffer mode. * BIOCGETBUFMODE Get current buffer mode. */ /* ARGSUSED */ static int bpfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct bpf_d *d; int error; error = devfs_get_cdevpriv((void **)&d); if (error != 0) return (error); /* * Refresh PID associated with this descriptor. */ BPFD_LOCK(d); BPF_PID_REFRESH(d, td); if (d->bd_state == BPF_WAITING) callout_stop(&d->bd_callout); d->bd_state = BPF_IDLE; BPFD_UNLOCK(d); if (d->bd_locked == 1) { switch (cmd) { case BIOCGBLEN: case BIOCFLUSH: case BIOCGDLT: case BIOCGDLTLIST: #ifdef COMPAT_FREEBSD32 case BIOCGDLTLIST32: #endif case BIOCGETIF: case BIOCGRTIMEOUT: #if defined(COMPAT_FREEBSD32) && !defined(__mips__) case BIOCGRTIMEOUT32: #endif case BIOCGSTATS: case BIOCVERSION: case BIOCGRSIG: case BIOCGHDRCMPLT: case BIOCSTSTAMP: case BIOCFEEDBACK: case FIONREAD: case BIOCLOCK: case BIOCSRTIMEOUT: #if defined(COMPAT_FREEBSD32) && !defined(__mips__) case BIOCSRTIMEOUT32: #endif case BIOCIMMEDIATE: case TIOCGPGRP: case BIOCROTZBUF: break; default: return (EPERM); } } #ifdef COMPAT_FREEBSD32 /* * If we see a 32-bit compat ioctl, mark the stream as 32-bit so * that it will get 32-bit packet headers. */ switch (cmd) { case BIOCSETF32: case BIOCSETFNR32: case BIOCSETWF32: case BIOCGDLTLIST32: case BIOCGRTIMEOUT32: case BIOCSRTIMEOUT32: BPFD_LOCK(d); d->bd_compat32 = 1; BPFD_UNLOCK(d); } #endif CURVNET_SET(TD_TO_VNET(td)); switch (cmd) { default: error = EINVAL; break; /* * Check for read packet available. */ case FIONREAD: { int n; BPFD_LOCK(d); n = d->bd_slen; while (d->bd_hbuf_in_use) mtx_sleep(&d->bd_hbuf_in_use, &d->bd_lock, PRINET, "bd_hbuf", 0); if (d->bd_hbuf) n += d->bd_hlen; BPFD_UNLOCK(d); *(int *)addr = n; break; } /* * Get buffer len [for read()]. */ case BIOCGBLEN: BPFD_LOCK(d); *(u_int *)addr = d->bd_bufsize; BPFD_UNLOCK(d); break; /* * Set buffer length. */ case BIOCSBLEN: error = bpf_ioctl_sblen(d, (u_int *)addr); break; /* * Set link layer read filter. */ case BIOCSETF: case BIOCSETFNR: case BIOCSETWF: #ifdef COMPAT_FREEBSD32 case BIOCSETF32: case BIOCSETFNR32: case BIOCSETWF32: #endif error = bpf_setf(d, (struct bpf_program *)addr, cmd); break; /* * Flush read packet buffer. */ case BIOCFLUSH: BPFD_LOCK(d); reset_d(d); BPFD_UNLOCK(d); break; /* * Put interface into promiscuous mode. */ case BIOCPROMISC: if (d->bd_bif == NULL) { /* * No interface attached yet. */ error = EINVAL; break; } if (d->bd_promisc == 0) { error = ifpromisc(d->bd_bif->bif_ifp, 1); if (error == 0) d->bd_promisc = 1; } break; /* * Get current data link type. */ case BIOCGDLT: BPF_LOCK(); if (d->bd_bif == NULL) error = EINVAL; else *(u_int *)addr = d->bd_bif->bif_dlt; BPF_UNLOCK(); break; /* * Get a list of supported data link types. */ #ifdef COMPAT_FREEBSD32 case BIOCGDLTLIST32: { struct bpf_dltlist32 *list32; struct bpf_dltlist dltlist; list32 = (struct bpf_dltlist32 *)addr; dltlist.bfl_len = list32->bfl_len; dltlist.bfl_list = PTRIN(list32->bfl_list); BPF_LOCK(); if (d->bd_bif == NULL) error = EINVAL; else { error = bpf_getdltlist(d, &dltlist); if (error == 0) list32->bfl_len = dltlist.bfl_len; } BPF_UNLOCK(); break; } #endif case BIOCGDLTLIST: BPF_LOCK(); if (d->bd_bif == NULL) error = EINVAL; else error = bpf_getdltlist(d, (struct bpf_dltlist *)addr); BPF_UNLOCK(); break; /* * Set data link type. */ case BIOCSDLT: BPF_LOCK(); if (d->bd_bif == NULL) error = EINVAL; else error = bpf_setdlt(d, *(u_int *)addr); BPF_UNLOCK(); break; /* * Get interface name. */ case BIOCGETIF: BPF_LOCK(); if (d->bd_bif == NULL) error = EINVAL; else { struct ifnet *const ifp = d->bd_bif->bif_ifp; struct ifreq *const ifr = (struct ifreq *)addr; strlcpy(ifr->ifr_name, ifp->if_xname, sizeof(ifr->ifr_name)); } BPF_UNLOCK(); break; /* * Set interface. */ case BIOCSETIF: { int alloc_buf, size; /* * Behavior here depends on the buffering model. If * we're using kernel memory buffers, then we can * allocate them here. If we're using zero-copy, * then the user process must have registered buffers * by the time we get here. */ alloc_buf = 0; BPFD_LOCK(d); if (d->bd_bufmode == BPF_BUFMODE_BUFFER && d->bd_sbuf == NULL) alloc_buf = 1; BPFD_UNLOCK(d); if (alloc_buf) { size = d->bd_bufsize; error = bpf_buffer_ioctl_sblen(d, &size); if (error != 0) break; } BPF_LOCK(); error = bpf_setif(d, (struct ifreq *)addr); BPF_UNLOCK(); break; } /* * Set read timeout. */ case BIOCSRTIMEOUT: #if defined(COMPAT_FREEBSD32) && !defined(__mips__) case BIOCSRTIMEOUT32: #endif { struct timeval *tv = (struct timeval *)addr; #if defined(COMPAT_FREEBSD32) && !defined(__mips__) struct timeval32 *tv32; struct timeval tv64; if (cmd == BIOCSRTIMEOUT32) { tv32 = (struct timeval32 *)addr; tv = &tv64; tv->tv_sec = tv32->tv_sec; tv->tv_usec = tv32->tv_usec; } else #endif tv = (struct timeval *)addr; /* * Subtract 1 tick from tvtohz() since this isn't * a one-shot timer. */ if ((error = itimerfix(tv)) == 0) d->bd_rtout = tvtohz(tv) - 1; break; } /* * Get read timeout. */ case BIOCGRTIMEOUT: #if defined(COMPAT_FREEBSD32) && !defined(__mips__) case BIOCGRTIMEOUT32: #endif { struct timeval *tv; #if defined(COMPAT_FREEBSD32) && !defined(__mips__) struct timeval32 *tv32; struct timeval tv64; if (cmd == BIOCGRTIMEOUT32) tv = &tv64; else #endif tv = (struct timeval *)addr; tv->tv_sec = d->bd_rtout / hz; tv->tv_usec = (d->bd_rtout % hz) * tick; #if defined(COMPAT_FREEBSD32) && !defined(__mips__) if (cmd == BIOCGRTIMEOUT32) { tv32 = (struct timeval32 *)addr; tv32->tv_sec = tv->tv_sec; tv32->tv_usec = tv->tv_usec; } #endif break; } /* * Get packet stats. */ case BIOCGSTATS: { struct bpf_stat *bs = (struct bpf_stat *)addr; /* XXXCSJP overflow */ bs->bs_recv = d->bd_rcount; bs->bs_drop = d->bd_dcount; break; } /* * Set immediate mode. */ case BIOCIMMEDIATE: BPFD_LOCK(d); d->bd_immediate = *(u_int *)addr; BPFD_UNLOCK(d); break; case BIOCVERSION: { struct bpf_version *bv = (struct bpf_version *)addr; bv->bv_major = BPF_MAJOR_VERSION; bv->bv_minor = BPF_MINOR_VERSION; break; } /* * Get "header already complete" flag */ case BIOCGHDRCMPLT: BPFD_LOCK(d); *(u_int *)addr = d->bd_hdrcmplt; BPFD_UNLOCK(d); break; /* * Set "header already complete" flag */ case BIOCSHDRCMPLT: BPFD_LOCK(d); d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0; BPFD_UNLOCK(d); break; /* * Get packet direction flag */ case BIOCGDIRECTION: BPFD_LOCK(d); *(u_int *)addr = d->bd_direction; BPFD_UNLOCK(d); break; /* * Set packet direction flag */ case BIOCSDIRECTION: { u_int direction; direction = *(u_int *)addr; switch (direction) { case BPF_D_IN: case BPF_D_INOUT: case BPF_D_OUT: BPFD_LOCK(d); d->bd_direction = direction; BPFD_UNLOCK(d); break; default: error = EINVAL; } } break; /* * Get packet timestamp format and resolution. */ case BIOCGTSTAMP: BPFD_LOCK(d); *(u_int *)addr = d->bd_tstamp; BPFD_UNLOCK(d); break; /* * Set packet timestamp format and resolution. */ case BIOCSTSTAMP: { u_int func; func = *(u_int *)addr; if (BPF_T_VALID(func)) d->bd_tstamp = func; else error = EINVAL; } break; case BIOCFEEDBACK: BPFD_LOCK(d); d->bd_feedback = *(u_int *)addr; BPFD_UNLOCK(d); break; case BIOCLOCK: BPFD_LOCK(d); d->bd_locked = 1; BPFD_UNLOCK(d); break; case FIONBIO: /* Non-blocking I/O */ break; case FIOASYNC: /* Send signal on receive packets */ BPFD_LOCK(d); d->bd_async = *(int *)addr; BPFD_UNLOCK(d); break; case FIOSETOWN: /* * XXX: Add some sort of locking here? * fsetown() can sleep. */ error = fsetown(*(int *)addr, &d->bd_sigio); break; case FIOGETOWN: BPFD_LOCK(d); *(int *)addr = fgetown(&d->bd_sigio); BPFD_UNLOCK(d); break; /* This is deprecated, FIOSETOWN should be used instead. */ case TIOCSPGRP: error = fsetown(-(*(int *)addr), &d->bd_sigio); break; /* This is deprecated, FIOGETOWN should be used instead. */ case TIOCGPGRP: *(int *)addr = -fgetown(&d->bd_sigio); break; case BIOCSRSIG: /* Set receive signal */ { u_int sig; sig = *(u_int *)addr; if (sig >= NSIG) error = EINVAL; else { BPFD_LOCK(d); d->bd_sig = sig; BPFD_UNLOCK(d); } break; } case BIOCGRSIG: BPFD_LOCK(d); *(u_int *)addr = d->bd_sig; BPFD_UNLOCK(d); break; case BIOCGETBUFMODE: BPFD_LOCK(d); *(u_int *)addr = d->bd_bufmode; BPFD_UNLOCK(d); break; case BIOCSETBUFMODE: /* * Allow the buffering mode to be changed as long as we * haven't yet committed to a particular mode. Our * definition of commitment, for now, is whether or not a * buffer has been allocated or an interface attached, since * that's the point where things get tricky. */ switch (*(u_int *)addr) { case BPF_BUFMODE_BUFFER: break; case BPF_BUFMODE_ZBUF: if (bpf_zerocopy_enable) break; /* FALLSTHROUGH */ default: CURVNET_RESTORE(); return (EINVAL); } BPFD_LOCK(d); if (d->bd_sbuf != NULL || d->bd_hbuf != NULL || d->bd_fbuf != NULL || d->bd_bif != NULL) { BPFD_UNLOCK(d); CURVNET_RESTORE(); return (EBUSY); } d->bd_bufmode = *(u_int *)addr; BPFD_UNLOCK(d); break; case BIOCGETZMAX: error = bpf_ioctl_getzmax(td, d, (size_t *)addr); break; case BIOCSETZBUF: error = bpf_ioctl_setzbuf(td, d, (struct bpf_zbuf *)addr); break; case BIOCROTZBUF: error = bpf_ioctl_rotzbuf(td, d, (struct bpf_zbuf *)addr); break; } CURVNET_RESTORE(); return (error); } /* * Set d's packet filter program to fp. If this file already has a filter, * free it and replace it. Returns EINVAL for bogus requests. * * Note we need global lock here to serialize bpf_setf() and bpf_setif() calls * since reading d->bd_bif can't be protected by d or interface lock due to * lock order. * * Additionally, we have to acquire interface write lock due to bpf_mtap() uses * interface read lock to read all filers. * */ static int bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd) { #ifdef COMPAT_FREEBSD32 struct bpf_program fp_swab; struct bpf_program32 *fp32; #endif struct bpf_insn *fcode, *old; #ifdef BPF_JITTER bpf_jit_filter *jfunc, *ofunc; #endif size_t size; u_int flen; int need_upgrade; #ifdef COMPAT_FREEBSD32 switch (cmd) { case BIOCSETF32: case BIOCSETWF32: case BIOCSETFNR32: fp32 = (struct bpf_program32 *)fp; fp_swab.bf_len = fp32->bf_len; fp_swab.bf_insns = (struct bpf_insn *)(uintptr_t)fp32->bf_insns; fp = &fp_swab; switch (cmd) { case BIOCSETF32: cmd = BIOCSETF; break; case BIOCSETWF32: cmd = BIOCSETWF; break; } break; } #endif fcode = NULL; #ifdef BPF_JITTER jfunc = ofunc = NULL; #endif need_upgrade = 0; /* * Check new filter validness before acquiring any locks. * Allocate memory for new filter, if needed. */ flen = fp->bf_len; if (flen > bpf_maxinsns || (fp->bf_insns == NULL && flen != 0)) return (EINVAL); size = flen * sizeof(*fp->bf_insns); if (size > 0) { /* We're setting up new filter. Copy and check actual data. */ fcode = malloc(size, M_BPF, M_WAITOK); if (copyin(fp->bf_insns, fcode, size) != 0 || !bpf_validate(fcode, flen)) { free(fcode, M_BPF); return (EINVAL); } #ifdef BPF_JITTER /* Filter is copied inside fcode and is perfectly valid. */ jfunc = bpf_jitter(fcode, flen); #endif } BPF_LOCK(); /* * Set up new filter. * Protect filter change by interface lock. * Additionally, we are protected by global lock here. */ if (d->bd_bif != NULL) BPFIF_WLOCK(d->bd_bif); BPFD_LOCK(d); if (cmd == BIOCSETWF) { old = d->bd_wfilter; d->bd_wfilter = fcode; } else { old = d->bd_rfilter; d->bd_rfilter = fcode; #ifdef BPF_JITTER ofunc = d->bd_bfilter; d->bd_bfilter = jfunc; #endif if (cmd == BIOCSETF) reset_d(d); need_upgrade = bpf_check_upgrade(cmd, d, fcode, flen); } BPFD_UNLOCK(d); if (d->bd_bif != NULL) BPFIF_WUNLOCK(d->bd_bif); if (old != NULL) free(old, M_BPF); #ifdef BPF_JITTER if (ofunc != NULL) bpf_destroy_jit_filter(ofunc); #endif /* Move d to active readers list. */ if (need_upgrade != 0) bpf_upgraded(d); BPF_UNLOCK(); return (0); } /* * Detach a file from its current interface (if attached at all) and attach * to the interface indicated by the name stored in ifr. * Return an errno or 0. */ static int bpf_setif(struct bpf_d *d, struct ifreq *ifr) { struct bpf_if *bp; struct ifnet *theywant; BPF_LOCK_ASSERT(); theywant = ifunit(ifr->ifr_name); if (theywant == NULL || theywant->if_bpf == NULL) return (ENXIO); bp = theywant->if_bpf; /* Check if interface is not being detached from BPF */ BPFIF_RLOCK(bp); if (bp->bif_flags & BPFIF_FLAG_DYING) { BPFIF_RUNLOCK(bp); return (ENXIO); } BPFIF_RUNLOCK(bp); /* * At this point, we expect the buffer is already allocated. If not, * return an error. */ switch (d->bd_bufmode) { case BPF_BUFMODE_BUFFER: case BPF_BUFMODE_ZBUF: if (d->bd_sbuf == NULL) return (EINVAL); break; default: panic("bpf_setif: bufmode %d", d->bd_bufmode); } if (bp != d->bd_bif) bpf_attachd(d, bp); BPFD_LOCK(d); reset_d(d); BPFD_UNLOCK(d); return (0); } /* * Support for select() and poll() system calls * * Return true iff the specific operation will not block indefinitely. * Otherwise, return false but make a note that a selwakeup() must be done. */ static int bpfpoll(struct cdev *dev, int events, struct thread *td) { struct bpf_d *d; int revents; if (devfs_get_cdevpriv((void **)&d) != 0 || d->bd_bif == NULL) return (events & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); /* * Refresh PID associated with this descriptor. */ revents = events & (POLLOUT | POLLWRNORM); BPFD_LOCK(d); BPF_PID_REFRESH(d, td); if (events & (POLLIN | POLLRDNORM)) { if (bpf_ready(d)) revents |= events & (POLLIN | POLLRDNORM); else { selrecord(td, &d->bd_sel); /* Start the read timeout if necessary. */ if (d->bd_rtout > 0 && d->bd_state == BPF_IDLE) { callout_reset(&d->bd_callout, d->bd_rtout, bpf_timed_out, d); d->bd_state = BPF_WAITING; } } } BPFD_UNLOCK(d); return (revents); } /* * Support for kevent() system call. Register EVFILT_READ filters and * reject all others. */ int bpfkqfilter(struct cdev *dev, struct knote *kn) { struct bpf_d *d; if (devfs_get_cdevpriv((void **)&d) != 0 || kn->kn_filter != EVFILT_READ) return (1); /* * Refresh PID associated with this descriptor. */ BPFD_LOCK(d); BPF_PID_REFRESH_CUR(d); kn->kn_fop = &bpfread_filtops; kn->kn_hook = d; knlist_add(&d->bd_sel.si_note, kn, 1); BPFD_UNLOCK(d); return (0); } static void filt_bpfdetach(struct knote *kn) { struct bpf_d *d = (struct bpf_d *)kn->kn_hook; knlist_remove(&d->bd_sel.si_note, kn, 0); } static int filt_bpfread(struct knote *kn, long hint) { struct bpf_d *d = (struct bpf_d *)kn->kn_hook; int ready; BPFD_LOCK_ASSERT(d); ready = bpf_ready(d); if (ready) { kn->kn_data = d->bd_slen; /* * Ignore the hold buffer if it is being copied to user space. */ if (!d->bd_hbuf_in_use && d->bd_hbuf) kn->kn_data += d->bd_hlen; } else if (d->bd_rtout > 0 && d->bd_state == BPF_IDLE) { callout_reset(&d->bd_callout, d->bd_rtout, bpf_timed_out, d); d->bd_state = BPF_WAITING; } return (ready); } #define BPF_TSTAMP_NONE 0 #define BPF_TSTAMP_FAST 1 #define BPF_TSTAMP_NORMAL 2 #define BPF_TSTAMP_EXTERN 3 static int bpf_ts_quality(int tstype) { if (tstype == BPF_T_NONE) return (BPF_TSTAMP_NONE); if ((tstype & BPF_T_FAST) != 0) return (BPF_TSTAMP_FAST); return (BPF_TSTAMP_NORMAL); } static int bpf_gettime(struct bintime *bt, int tstype, struct mbuf *m) { struct m_tag *tag; int quality; quality = bpf_ts_quality(tstype); if (quality == BPF_TSTAMP_NONE) return (quality); if (m != NULL) { tag = m_tag_locate(m, MTAG_BPF, MTAG_BPF_TIMESTAMP, NULL); if (tag != NULL) { *bt = *(struct bintime *)(tag + 1); return (BPF_TSTAMP_EXTERN); } } if (quality == BPF_TSTAMP_NORMAL) binuptime(bt); else getbinuptime(bt); return (quality); } /* * Incoming linkage from device drivers. Process the packet pkt, of length * pktlen, which is stored in a contiguous buffer. The packet is parsed * by each process' filter, and if accepted, stashed into the corresponding * buffer. */ void bpf_tap(struct bpf_if *bp, u_char *pkt, u_int pktlen) { struct bintime bt; struct bpf_d *d; #ifdef BPF_JITTER bpf_jit_filter *bf; #endif u_int slen; int gottime; gottime = BPF_TSTAMP_NONE; BPFIF_RLOCK(bp); LIST_FOREACH(d, &bp->bif_dlist, bd_next) { /* * We are not using any locks for d here because: * 1) any filter change is protected by interface * write lock * 2) destroying/detaching d is protected by interface * write lock, too */ /* XXX: Do not protect counter for the sake of performance. */ ++d->bd_rcount; /* * NB: We dont call BPF_CHECK_DIRECTION() here since there is no * way for the caller to indiciate to us whether this packet * is inbound or outbound. In the bpf_mtap() routines, we use * the interface pointers on the mbuf to figure it out. */ #ifdef BPF_JITTER bf = bpf_jitter_enable != 0 ? d->bd_bfilter : NULL; if (bf != NULL) slen = (*(bf->func))(pkt, pktlen, pktlen); else #endif slen = bpf_filter(d->bd_rfilter, pkt, pktlen, pktlen); if (slen != 0) { /* * Filter matches. Let's to acquire write lock. */ BPFD_LOCK(d); d->bd_fcount++; if (gottime < bpf_ts_quality(d->bd_tstamp)) gottime = bpf_gettime(&bt, d->bd_tstamp, NULL); #ifdef MAC if (mac_bpfdesc_check_receive(d, bp->bif_ifp) == 0) #endif catchpacket(d, pkt, pktlen, slen, bpf_append_bytes, &bt); BPFD_UNLOCK(d); } } BPFIF_RUNLOCK(bp); } #define BPF_CHECK_DIRECTION(d, r, i) \ (((d)->bd_direction == BPF_D_IN && (r) != (i)) || \ ((d)->bd_direction == BPF_D_OUT && (r) == (i))) /* * Incoming linkage from device drivers, when packet is in an mbuf chain. * Locking model is explained in bpf_tap(). */ void bpf_mtap(struct bpf_if *bp, struct mbuf *m) { struct bintime bt; struct bpf_d *d; #ifdef BPF_JITTER bpf_jit_filter *bf; #endif u_int pktlen, slen; int gottime; /* Skip outgoing duplicate packets. */ if ((m->m_flags & M_PROMISC) != 0 && m->m_pkthdr.rcvif == NULL) { m->m_flags &= ~M_PROMISC; return; } pktlen = m_length(m, NULL); gottime = BPF_TSTAMP_NONE; BPFIF_RLOCK(bp); LIST_FOREACH(d, &bp->bif_dlist, bd_next) { if (BPF_CHECK_DIRECTION(d, m->m_pkthdr.rcvif, bp->bif_ifp)) continue; ++d->bd_rcount; #ifdef BPF_JITTER bf = bpf_jitter_enable != 0 ? d->bd_bfilter : NULL; /* XXX We cannot handle multiple mbufs. */ if (bf != NULL && m->m_next == NULL) slen = (*(bf->func))(mtod(m, u_char *), pktlen, pktlen); else #endif slen = bpf_filter(d->bd_rfilter, (u_char *)m, pktlen, 0); if (slen != 0) { BPFD_LOCK(d); d->bd_fcount++; if (gottime < bpf_ts_quality(d->bd_tstamp)) gottime = bpf_gettime(&bt, d->bd_tstamp, m); #ifdef MAC if (mac_bpfdesc_check_receive(d, bp->bif_ifp) == 0) #endif catchpacket(d, (u_char *)m, pktlen, slen, bpf_append_mbuf, &bt); BPFD_UNLOCK(d); } } BPFIF_RUNLOCK(bp); } /* * Incoming linkage from device drivers, when packet is in * an mbuf chain and to be prepended by a contiguous header. */ void bpf_mtap2(struct bpf_if *bp, void *data, u_int dlen, struct mbuf *m) { struct bintime bt; struct mbuf mb; struct bpf_d *d; u_int pktlen, slen; int gottime; /* Skip outgoing duplicate packets. */ if ((m->m_flags & M_PROMISC) != 0 && m->m_pkthdr.rcvif == NULL) { m->m_flags &= ~M_PROMISC; return; } pktlen = m_length(m, NULL); /* * Craft on-stack mbuf suitable for passing to bpf_filter. * Note that we cut corners here; we only setup what's * absolutely needed--this mbuf should never go anywhere else. */ mb.m_next = m; mb.m_data = data; mb.m_len = dlen; pktlen += dlen; gottime = BPF_TSTAMP_NONE; BPFIF_RLOCK(bp); LIST_FOREACH(d, &bp->bif_dlist, bd_next) { if (BPF_CHECK_DIRECTION(d, m->m_pkthdr.rcvif, bp->bif_ifp)) continue; ++d->bd_rcount; slen = bpf_filter(d->bd_rfilter, (u_char *)&mb, pktlen, 0); if (slen != 0) { BPFD_LOCK(d); d->bd_fcount++; if (gottime < bpf_ts_quality(d->bd_tstamp)) gottime = bpf_gettime(&bt, d->bd_tstamp, m); #ifdef MAC if (mac_bpfdesc_check_receive(d, bp->bif_ifp) == 0) #endif catchpacket(d, (u_char *)&mb, pktlen, slen, bpf_append_mbuf, &bt); BPFD_UNLOCK(d); } } BPFIF_RUNLOCK(bp); } #undef BPF_CHECK_DIRECTION #undef BPF_TSTAMP_NONE #undef BPF_TSTAMP_FAST #undef BPF_TSTAMP_NORMAL #undef BPF_TSTAMP_EXTERN static int bpf_hdrlen(struct bpf_d *d) { int hdrlen; hdrlen = d->bd_bif->bif_hdrlen; #ifndef BURN_BRIDGES if (d->bd_tstamp == BPF_T_NONE || BPF_T_FORMAT(d->bd_tstamp) == BPF_T_MICROTIME) #ifdef COMPAT_FREEBSD32 if (d->bd_compat32) hdrlen += SIZEOF_BPF_HDR(struct bpf_hdr32); else #endif hdrlen += SIZEOF_BPF_HDR(struct bpf_hdr); else #endif hdrlen += SIZEOF_BPF_HDR(struct bpf_xhdr); #ifdef COMPAT_FREEBSD32 if (d->bd_compat32) hdrlen = BPF_WORDALIGN32(hdrlen); else #endif hdrlen = BPF_WORDALIGN(hdrlen); return (hdrlen - d->bd_bif->bif_hdrlen); } static void bpf_bintime2ts(struct bintime *bt, struct bpf_ts *ts, int tstype) { struct bintime bt2; struct timeval tsm; struct timespec tsn; if ((tstype & BPF_T_MONOTONIC) == 0) { bt2 = *bt; bintime_add(&bt2, &boottimebin); bt = &bt2; } switch (BPF_T_FORMAT(tstype)) { case BPF_T_MICROTIME: bintime2timeval(bt, &tsm); ts->bt_sec = tsm.tv_sec; ts->bt_frac = tsm.tv_usec; break; case BPF_T_NANOTIME: bintime2timespec(bt, &tsn); ts->bt_sec = tsn.tv_sec; ts->bt_frac = tsn.tv_nsec; break; case BPF_T_BINTIME: ts->bt_sec = bt->sec; ts->bt_frac = bt->frac; break; } } /* * Move the packet data from interface memory (pkt) into the * store buffer. "cpfn" is the routine called to do the actual data * transfer. bcopy is passed in to copy contiguous chunks, while * bpf_append_mbuf is passed in to copy mbuf chains. In the latter case, * pkt is really an mbuf. */ static void catchpacket(struct bpf_d *d, u_char *pkt, u_int pktlen, u_int snaplen, void (*cpfn)(struct bpf_d *, caddr_t, u_int, void *, u_int), struct bintime *bt) { struct bpf_xhdr hdr; #ifndef BURN_BRIDGES struct bpf_hdr hdr_old; #ifdef COMPAT_FREEBSD32 struct bpf_hdr32 hdr32_old; #endif #endif int caplen, curlen, hdrlen, totlen; int do_wakeup = 0; int do_timestamp; int tstype; BPFD_LOCK_ASSERT(d); /* * Detect whether user space has released a buffer back to us, and if * so, move it from being a hold buffer to a free buffer. This may * not be the best place to do it (for example, we might only want to * run this check if we need the space), but for now it's a reliable * spot to do it. */ if (d->bd_fbuf == NULL && bpf_canfreebuf(d)) { d->bd_fbuf = d->bd_hbuf; d->bd_hbuf = NULL; d->bd_hlen = 0; bpf_buf_reclaimed(d); } /* * Figure out how many bytes to move. If the packet is * greater or equal to the snapshot length, transfer that * much. Otherwise, transfer the whole packet (unless * we hit the buffer size limit). */ hdrlen = bpf_hdrlen(d); totlen = hdrlen + min(snaplen, pktlen); if (totlen > d->bd_bufsize) totlen = d->bd_bufsize; /* * Round up the end of the previous packet to the next longword. * * Drop the packet if there's no room and no hope of room * If the packet would overflow the storage buffer or the storage * buffer is considered immutable by the buffer model, try to rotate * the buffer and wakeup pending processes. */ #ifdef COMPAT_FREEBSD32 if (d->bd_compat32) curlen = BPF_WORDALIGN32(d->bd_slen); else #endif curlen = BPF_WORDALIGN(d->bd_slen); if (curlen + totlen > d->bd_bufsize || !bpf_canwritebuf(d)) { if (d->bd_fbuf == NULL) { /* * There's no room in the store buffer, and no * prospect of room, so drop the packet. Notify the * buffer model. */ bpf_buffull(d); ++d->bd_dcount; return; } KASSERT(!d->bd_hbuf_in_use, ("hold buffer is in use")); ROTATE_BUFFERS(d); do_wakeup = 1; curlen = 0; } else if (d->bd_immediate || d->bd_state == BPF_TIMED_OUT) /* * Immediate mode is set, or the read timeout has already * expired during a select call. A packet arrived, so the * reader should be woken up. */ do_wakeup = 1; caplen = totlen - hdrlen; tstype = d->bd_tstamp; do_timestamp = tstype != BPF_T_NONE; #ifndef BURN_BRIDGES if (tstype == BPF_T_NONE || BPF_T_FORMAT(tstype) == BPF_T_MICROTIME) { struct bpf_ts ts; if (do_timestamp) bpf_bintime2ts(bt, &ts, tstype); #ifdef COMPAT_FREEBSD32 if (d->bd_compat32) { bzero(&hdr32_old, sizeof(hdr32_old)); if (do_timestamp) { hdr32_old.bh_tstamp.tv_sec = ts.bt_sec; hdr32_old.bh_tstamp.tv_usec = ts.bt_frac; } hdr32_old.bh_datalen = pktlen; hdr32_old.bh_hdrlen = hdrlen; hdr32_old.bh_caplen = caplen; bpf_append_bytes(d, d->bd_sbuf, curlen, &hdr32_old, sizeof(hdr32_old)); goto copy; } #endif bzero(&hdr_old, sizeof(hdr_old)); if (do_timestamp) { hdr_old.bh_tstamp.tv_sec = ts.bt_sec; hdr_old.bh_tstamp.tv_usec = ts.bt_frac; } hdr_old.bh_datalen = pktlen; hdr_old.bh_hdrlen = hdrlen; hdr_old.bh_caplen = caplen; bpf_append_bytes(d, d->bd_sbuf, curlen, &hdr_old, sizeof(hdr_old)); goto copy; } #endif /* * Append the bpf header. Note we append the actual header size, but * move forward the length of the header plus padding. */ bzero(&hdr, sizeof(hdr)); if (do_timestamp) bpf_bintime2ts(bt, &hdr.bh_tstamp, tstype); hdr.bh_datalen = pktlen; hdr.bh_hdrlen = hdrlen; hdr.bh_caplen = caplen; bpf_append_bytes(d, d->bd_sbuf, curlen, &hdr, sizeof(hdr)); /* * Copy the packet data into the store buffer and update its length. */ #ifndef BURN_BRIDGES copy: #endif (*cpfn)(d, d->bd_sbuf, curlen + hdrlen, pkt, caplen); d->bd_slen = curlen + totlen; if (do_wakeup) bpf_wakeup(d); } /* * Free buffers currently in use by a descriptor. * Called on close. */ static void bpf_freed(struct bpf_d *d) { /* * We don't need to lock out interrupts since this descriptor has * been detached from its interface and it yet hasn't been marked * free. */ bpf_free(d); if (d->bd_rfilter != NULL) { free((caddr_t)d->bd_rfilter, M_BPF); #ifdef BPF_JITTER if (d->bd_bfilter != NULL) bpf_destroy_jit_filter(d->bd_bfilter); #endif } if (d->bd_wfilter != NULL) free((caddr_t)d->bd_wfilter, M_BPF); mtx_destroy(&d->bd_lock); } /* * Attach an interface to bpf. dlt is the link layer type; hdrlen is the * fixed size of the link header (variable length headers not yet supported). */ void bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen) { bpfattach2(ifp, dlt, hdrlen, &ifp->if_bpf); } /* * Attach an interface to bpf. ifp is a pointer to the structure * defining the interface to be attached, dlt is the link layer type, * and hdrlen is the fixed size of the link header (variable length * headers are not yet supporrted). */ void bpfattach2(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp) { struct bpf_if *bp; bp = malloc(sizeof(*bp), M_BPF, M_NOWAIT | M_ZERO); if (bp == NULL) panic("bpfattach"); LIST_INIT(&bp->bif_dlist); LIST_INIT(&bp->bif_wlist); bp->bif_ifp = ifp; bp->bif_dlt = dlt; rw_init(&bp->bif_lock, "bpf interface lock"); KASSERT(*driverp == NULL, ("bpfattach2: driverp already initialized")); *driverp = bp; BPF_LOCK(); LIST_INSERT_HEAD(&bpf_iflist, bp, bif_next); BPF_UNLOCK(); bp->bif_hdrlen = hdrlen; if (bootverbose && IS_DEFAULT_VNET(curvnet)) if_printf(ifp, "bpf attached\n"); } /* * Detach bpf from an interface. This involves detaching each descriptor * associated with the interface. Notify each descriptor as it's detached * so that any sleepers wake up and get ENXIO. */ void bpfdetach(struct ifnet *ifp) { struct bpf_if *bp, *bp_temp; struct bpf_d *d; int ndetached; ndetached = 0; BPF_LOCK(); /* Find all bpf_if struct's which reference ifp and detach them. */ LIST_FOREACH_SAFE(bp, &bpf_iflist, bif_next, bp_temp) { if (ifp != bp->bif_ifp) continue; LIST_REMOVE(bp, bif_next); /* Add to to-be-freed list */ LIST_INSERT_HEAD(&bpf_freelist, bp, bif_next); ndetached++; /* * Delay freeing bp till interface is detached * and all routes through this interface are removed. * Mark bp as detached to restrict new consumers. */ BPFIF_WLOCK(bp); bp->bif_flags |= BPFIF_FLAG_DYING; BPFIF_WUNLOCK(bp); CTR4(KTR_NET, "%s: sheduling free for encap %d (%p) for if %p", __func__, bp->bif_dlt, bp, ifp); /* Free common descriptors */ while ((d = LIST_FIRST(&bp->bif_dlist)) != NULL) { bpf_detachd_locked(d); BPFD_LOCK(d); bpf_wakeup(d); BPFD_UNLOCK(d); } /* Free writer-only descriptors */ while ((d = LIST_FIRST(&bp->bif_wlist)) != NULL) { bpf_detachd_locked(d); BPFD_LOCK(d); bpf_wakeup(d); BPFD_UNLOCK(d); } } BPF_UNLOCK(); #ifdef INVARIANTS if (ndetached == 0) printf("bpfdetach: %s was not attached\n", ifp->if_xname); #endif } /* * Interface departure handler. * Note departure event does not guarantee interface is going down. * Interface renaming is currently done via departure/arrival event set. * * Departure handled is called after all routes pointing to * given interface are removed and interface is in down state * restricting any packets to be sent/received. We assume it is now safe * to free data allocated by BPF. */ static void bpf_ifdetach(void *arg __unused, struct ifnet *ifp) { struct bpf_if *bp, *bp_temp; int nmatched = 0; BPF_LOCK(); /* * Find matching entries in free list. * Nothing should be found if bpfdetach() was not called. */ LIST_FOREACH_SAFE(bp, &bpf_freelist, bif_next, bp_temp) { if (ifp != bp->bif_ifp) continue; CTR3(KTR_NET, "%s: freeing BPF instance %p for interface %p", __func__, bp, ifp); LIST_REMOVE(bp, bif_next); rw_destroy(&bp->bif_lock); free(bp, M_BPF); nmatched++; } BPF_UNLOCK(); /* * Note that we cannot zero other pointers to * custom DLTs possibly used by given interface. */ if (nmatched != 0) ifp->if_bpf = NULL; } /* * Get a list of available data link type of the interface. */ static int bpf_getdltlist(struct bpf_d *d, struct bpf_dltlist *bfl) { int n, error; struct ifnet *ifp; struct bpf_if *bp; BPF_LOCK_ASSERT(); ifp = d->bd_bif->bif_ifp; n = 0; error = 0; LIST_FOREACH(bp, &bpf_iflist, bif_next) { if (bp->bif_ifp != ifp) continue; if (bfl->bfl_list != NULL) { if (n >= bfl->bfl_len) return (ENOMEM); error = copyout(&bp->bif_dlt, bfl->bfl_list + n, sizeof(u_int)); } n++; } bfl->bfl_len = n; return (error); } /* * Set the data link type of a BPF instance. */ static int bpf_setdlt(struct bpf_d *d, u_int dlt) { int error, opromisc; struct ifnet *ifp; struct bpf_if *bp; BPF_LOCK_ASSERT(); if (d->bd_bif->bif_dlt == dlt) return (0); ifp = d->bd_bif->bif_ifp; LIST_FOREACH(bp, &bpf_iflist, bif_next) { if (bp->bif_ifp == ifp && bp->bif_dlt == dlt) break; } if (bp != NULL) { opromisc = d->bd_promisc; bpf_attachd(d, bp); BPFD_LOCK(d); reset_d(d); BPFD_UNLOCK(d); if (opromisc) { error = ifpromisc(bp->bif_ifp, 1); if (error) if_printf(bp->bif_ifp, "bpf_setdlt: ifpromisc failed (%d)\n", error); else d->bd_promisc = 1; } } return (bp == NULL ? EINVAL : 0); } static void bpf_drvinit(void *unused) { struct cdev *dev; mtx_init(&bpf_mtx, "bpf global lock", NULL, MTX_DEF); LIST_INIT(&bpf_iflist); LIST_INIT(&bpf_freelist); dev = make_dev(&bpf_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "bpf"); /* For compatibility */ make_dev_alias(dev, "bpf0"); /* Register interface departure handler */ bpf_ifdetach_cookie = EVENTHANDLER_REGISTER( ifnet_departure_event, bpf_ifdetach, NULL, EVENTHANDLER_PRI_ANY); } /* * Zero out the various packet counters associated with all of the bpf * descriptors. At some point, we will probably want to get a bit more * granular and allow the user to specify descriptors to be zeroed. */ static void bpf_zero_counters(void) { struct bpf_if *bp; struct bpf_d *bd; BPF_LOCK(); LIST_FOREACH(bp, &bpf_iflist, bif_next) { BPFIF_RLOCK(bp); LIST_FOREACH(bd, &bp->bif_dlist, bd_next) { BPFD_LOCK(bd); bd->bd_rcount = 0; bd->bd_dcount = 0; bd->bd_fcount = 0; bd->bd_wcount = 0; bd->bd_wfcount = 0; bd->bd_zcopy = 0; BPFD_UNLOCK(bd); } BPFIF_RUNLOCK(bp); } BPF_UNLOCK(); } /* * Fill filter statistics */ static void bpfstats_fill_xbpf(struct xbpf_d *d, struct bpf_d *bd) { bzero(d, sizeof(*d)); BPFD_LOCK_ASSERT(bd); d->bd_structsize = sizeof(*d); /* XXX: reading should be protected by global lock */ d->bd_immediate = bd->bd_immediate; d->bd_promisc = bd->bd_promisc; d->bd_hdrcmplt = bd->bd_hdrcmplt; d->bd_direction = bd->bd_direction; d->bd_feedback = bd->bd_feedback; d->bd_async = bd->bd_async; d->bd_rcount = bd->bd_rcount; d->bd_dcount = bd->bd_dcount; d->bd_fcount = bd->bd_fcount; d->bd_sig = bd->bd_sig; d->bd_slen = bd->bd_slen; d->bd_hlen = bd->bd_hlen; d->bd_bufsize = bd->bd_bufsize; d->bd_pid = bd->bd_pid; strlcpy(d->bd_ifname, bd->bd_bif->bif_ifp->if_xname, IFNAMSIZ); d->bd_locked = bd->bd_locked; d->bd_wcount = bd->bd_wcount; d->bd_wdcount = bd->bd_wdcount; d->bd_wfcount = bd->bd_wfcount; d->bd_zcopy = bd->bd_zcopy; d->bd_bufmode = bd->bd_bufmode; } /* * Handle `netstat -B' stats request */ static int bpf_stats_sysctl(SYSCTL_HANDLER_ARGS) { static const struct xbpf_d zerostats; struct xbpf_d *xbdbuf, *xbd, tempstats; int index, error; struct bpf_if *bp; struct bpf_d *bd; /* * XXX This is not technically correct. It is possible for non * privileged users to open bpf devices. It would make sense * if the users who opened the devices were able to retrieve * the statistics for them, too. */ error = priv_check(req->td, PRIV_NET_BPF); if (error) return (error); /* * Check to see if the user is requesting that the counters be * zeroed out. Explicitly check that the supplied data is zeroed, * as we aren't allowing the user to set the counters currently. */ if (req->newptr != NULL) { if (req->newlen != sizeof(tempstats)) return (EINVAL); memset(&tempstats, 0, sizeof(tempstats)); error = SYSCTL_IN(req, &tempstats, sizeof(tempstats)); if (error) return (error); if (bcmp(&tempstats, &zerostats, sizeof(tempstats)) != 0) return (EINVAL); bpf_zero_counters(); return (0); } if (req->oldptr == NULL) return (SYSCTL_OUT(req, 0, bpf_bpfd_cnt * sizeof(*xbd))); if (bpf_bpfd_cnt == 0) return (SYSCTL_OUT(req, 0, 0)); xbdbuf = malloc(req->oldlen, M_BPF, M_WAITOK); BPF_LOCK(); if (req->oldlen < (bpf_bpfd_cnt * sizeof(*xbd))) { BPF_UNLOCK(); free(xbdbuf, M_BPF); return (ENOMEM); } index = 0; LIST_FOREACH(bp, &bpf_iflist, bif_next) { BPFIF_RLOCK(bp); /* Send writers-only first */ LIST_FOREACH(bd, &bp->bif_wlist, bd_next) { xbd = &xbdbuf[index++]; BPFD_LOCK(bd); bpfstats_fill_xbpf(xbd, bd); BPFD_UNLOCK(bd); } LIST_FOREACH(bd, &bp->bif_dlist, bd_next) { xbd = &xbdbuf[index++]; BPFD_LOCK(bd); bpfstats_fill_xbpf(xbd, bd); BPFD_UNLOCK(bd); } BPFIF_RUNLOCK(bp); } BPF_UNLOCK(); error = SYSCTL_OUT(req, xbdbuf, index * sizeof(*xbd)); free(xbdbuf, M_BPF); return (error); } SYSINIT(bpfdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,bpf_drvinit,NULL); #else /* !DEV_BPF && !NETGRAPH_BPF */ /* * NOP stubs to allow bpf-using drivers to load and function. * * A 'better' implementation would allow the core bpf functionality * to be loaded at runtime. */ static struct bpf_if bp_null; void bpf_tap(struct bpf_if *bp, u_char *pkt, u_int pktlen) { } void bpf_mtap(struct bpf_if *bp, struct mbuf *m) { } void bpf_mtap2(struct bpf_if *bp, void *d, u_int l, struct mbuf *m) { } void bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen) { bpfattach2(ifp, dlt, hdrlen, &ifp->if_bpf); } void bpfattach2(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp) { *driverp = &bp_null; } void bpfdetach(struct ifnet *ifp) { } u_int bpf_filter(const struct bpf_insn *pc, u_char *p, u_int wirelen, u_int buflen) { return -1; /* "no filter" behaviour */ } int bpf_validate(const struct bpf_insn *f, int len) { return 0; /* false */ } #endif /* !DEV_BPF && !NETGRAPH_BPF */ Index: projects/clang380-import/sys/net/flowtable.c =================================================================== --- projects/clang380-import/sys/net/flowtable.c (revision 293015) +++ projects/clang380-import/sys/net/flowtable.c (revision 293016) @@ -1,1186 +1,1194 @@ /*- * Copyright (c) 2014 Gleb Smirnoff * Copyright (c) 2008-2010, BitGravity 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. * * 2. Neither the name of the BitGravity Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "opt_route.h" #include "opt_mpath.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_inet6.h" #include __FBSDID("$FreeBSD$"); #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 #include #include #include #include #include #ifdef INET6 #include #endif #ifdef FLOWTABLE_HASH_ALL #include #include #include #endif #include #ifdef FLOWTABLE_HASH_ALL #define KEY_PORTS (sizeof(uint16_t) * 2) #define KEY_ADDRS 2 #else #define KEY_PORTS 0 #define KEY_ADDRS 1 #endif #ifdef INET6 #define KEY_ADDR_LEN sizeof(struct in6_addr) #else #define KEY_ADDR_LEN sizeof(struct in_addr) #endif #define KEYLEN ((KEY_ADDR_LEN * KEY_ADDRS + KEY_PORTS) / sizeof(uint32_t)) struct flentry { uint32_t f_hash; /* hash flowing forward */ uint32_t f_key[KEYLEN]; /* address(es and ports) */ uint32_t f_uptime; /* uptime at last access */ uint16_t f_fibnum; /* fib index */ #ifdef FLOWTABLE_HASH_ALL uint8_t f_proto; /* protocol */ uint8_t f_flags; /* stale? */ #define FL_STALE 1 #endif SLIST_ENTRY(flentry) f_next; /* pointer to collision entry */ struct rtentry *f_rt; /* rtentry for flow */ struct llentry *f_lle; /* llentry for flow */ }; #undef KEYLEN SLIST_HEAD(flist, flentry); /* Make sure we can use pcpu_zone_ptr for struct flist. */ CTASSERT(sizeof(struct flist) == sizeof(void *)); struct flowtable { counter_u64_t *ft_stat; int ft_size; /* * ft_table is a malloc(9)ed array of pointers. Pointers point to * memory from UMA_ZONE_PCPU zone. * ft_masks is per-cpu pointer itself. Each instance points * to a malloc(9)ed bitset, that is private to corresponding CPU. */ struct flist **ft_table; bitstr_t **ft_masks; bitstr_t *ft_tmpmask; }; #define FLOWSTAT_ADD(ft, name, v) \ counter_u64_add((ft)->ft_stat[offsetof(struct flowtable_stat, name) / sizeof(uint64_t)], (v)) #define FLOWSTAT_INC(ft, name) FLOWSTAT_ADD(ft, name, 1) static struct proc *flowcleanerproc; static uint32_t flow_hashjitter; static struct cv flowclean_f_cv; static struct cv flowclean_c_cv; static struct mtx flowclean_lock; static uint32_t flowclean_cycles; /* * TODO: * - add sysctls to resize && flush flow tables * - Add per flowtable sysctls for statistics and configuring timeouts * - add saturation counter to rtentry to support per-packet load-balancing * add flag to indicate round-robin flow, add list lookup from head for flows * - add sysctl / device node / syscall to support exporting and importing * of flows with flag to indicate that a flow was imported so should * not be considered for auto-cleaning * - support explicit connection state (currently only ad-hoc for DSR) * - idetach() cleanup for options VIMAGE builds. */ #ifdef INET static VNET_DEFINE(struct flowtable, ip4_ft); #define V_ip4_ft VNET(ip4_ft) #endif #ifdef INET6 static VNET_DEFINE(struct flowtable, ip6_ft); #define V_ip6_ft VNET(ip6_ft) #endif static uma_zone_t flow_zone; static VNET_DEFINE(int, flowtable_enable) = 1; #define V_flowtable_enable VNET(flowtable_enable) static SYSCTL_NODE(_net, OID_AUTO, flowtable, CTLFLAG_RD, NULL, "flowtable"); SYSCTL_INT(_net_flowtable, OID_AUTO, enable, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(flowtable_enable), 0, "enable flowtable caching."); SYSCTL_UMA_MAX(_net_flowtable, OID_AUTO, maxflows, CTLFLAG_RW, &flow_zone, "Maximum number of flows allowed"); static MALLOC_DEFINE(M_FTABLE, "flowtable", "flowtable hashes and bitstrings"); static struct flentry * flowtable_lookup_common(struct flowtable *, uint32_t *, int, uint32_t); #ifdef INET static struct flentry * flowtable_lookup_ipv4(struct mbuf *m, struct route *ro) { struct flentry *fle; struct sockaddr_in *sin; struct ip *ip; uint32_t fibnum; #ifdef FLOWTABLE_HASH_ALL uint32_t key[3]; int iphlen; uint16_t sport, dport; uint8_t proto; #endif ip = mtod(m, struct ip *); if (ip->ip_src.s_addr == ip->ip_dst.s_addr || (ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) return (NULL); fibnum = M_GETFIB(m); #ifdef FLOWTABLE_HASH_ALL iphlen = ip->ip_hl << 2; proto = ip->ip_p; switch (proto) { case IPPROTO_TCP: { struct tcphdr *th; th = (struct tcphdr *)((char *)ip + iphlen); sport = th->th_sport; dport = th->th_dport; if (th->th_flags & (TH_RST|TH_FIN)) fibnum |= (FL_STALE << 24); break; } case IPPROTO_UDP: { struct udphdr *uh; uh = (struct udphdr *)((char *)ip + iphlen); sport = uh->uh_sport; dport = uh->uh_dport; break; } case IPPROTO_SCTP: { struct sctphdr *sh; sh = (struct sctphdr *)((char *)ip + iphlen); sport = sh->src_port; dport = sh->dest_port; /* XXXGL: handle stale? */ break; } default: sport = dport = 0; break; } key[0] = ip->ip_dst.s_addr; key[1] = ip->ip_src.s_addr; key[2] = (dport << 16) | sport; fibnum |= proto << 16; fle = flowtable_lookup_common(&V_ip4_ft, key, 3 * sizeof(uint32_t), fibnum); #else /* !FLOWTABLE_HASH_ALL */ fle = flowtable_lookup_common(&V_ip4_ft, (uint32_t *)&ip->ip_dst, sizeof(struct in_addr), fibnum); #endif /* FLOWTABLE_HASH_ALL */ if (fle == NULL) return (NULL); sin = (struct sockaddr_in *)&ro->ro_dst; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = ip->ip_dst; return (fle); } #endif /* INET */ #ifdef INET6 /* * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, * then it sets p to point at the offset "len" in the mbuf. WARNING: the * pointer might become stale after other pullups (but we never use it * this way). */ #define PULLUP_TO(_len, p, T) \ do { \ int x = (_len) + sizeof(T); \ if ((m)->m_len < x) \ return (NULL); \ p = (mtod(m, char *) + (_len)); \ } while (0) #define TCP(p) ((struct tcphdr *)(p)) #define SCTP(p) ((struct sctphdr *)(p)) #define UDP(p) ((struct udphdr *)(p)) static struct flentry * flowtable_lookup_ipv6(struct mbuf *m, struct route *ro) { struct flentry *fle; struct sockaddr_in6 *sin6; struct ip6_hdr *ip6; uint32_t fibnum; #ifdef FLOWTABLE_HASH_ALL uint32_t key[9]; void *ulp; int hlen; uint16_t sport, dport; u_short offset; uint8_t proto; #else uint32_t key[4]; #endif ip6 = mtod(m, struct ip6_hdr *); if (in6_localaddr(&ip6->ip6_dst)) return (NULL); fibnum = M_GETFIB(m); #ifdef FLOWTABLE_HASH_ALL hlen = sizeof(struct ip6_hdr); proto = ip6->ip6_nxt; offset = sport = dport = 0; ulp = NULL; while (ulp == NULL) { switch (proto) { case IPPROTO_ICMPV6: case IPPROTO_OSPFIGP: case IPPROTO_PIM: case IPPROTO_CARP: case IPPROTO_ESP: case IPPROTO_NONE: ulp = ip6; break; case IPPROTO_TCP: PULLUP_TO(hlen, ulp, struct tcphdr); dport = TCP(ulp)->th_dport; sport = TCP(ulp)->th_sport; if (TCP(ulp)->th_flags & (TH_RST|TH_FIN)) fibnum |= (FL_STALE << 24); break; case IPPROTO_SCTP: PULLUP_TO(hlen, ulp, struct sctphdr); dport = SCTP(ulp)->src_port; sport = SCTP(ulp)->dest_port; /* XXXGL: handle stale? */ break; case IPPROTO_UDP: PULLUP_TO(hlen, ulp, struct udphdr); dport = UDP(ulp)->uh_dport; sport = UDP(ulp)->uh_sport; break; case IPPROTO_HOPOPTS: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_hbh); hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; ulp = NULL; break; case IPPROTO_ROUTING: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_rthdr); hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; ulp = NULL; break; case IPPROTO_FRAGMENT: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_frag); hlen += sizeof (struct ip6_frag); proto = ((struct ip6_frag *)ulp)->ip6f_nxt; offset = ((struct ip6_frag *)ulp)->ip6f_offlg & IP6F_OFF_MASK; ulp = NULL; break; case IPPROTO_DSTOPTS: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_hbh); hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; ulp = NULL; break; case IPPROTO_AH: /* RFC 2402 */ PULLUP_TO(hlen, ulp, struct ip6_ext); hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; proto = ((struct ip6_ext *)ulp)->ip6e_nxt; ulp = NULL; break; default: PULLUP_TO(hlen, ulp, struct ip6_ext); break; } } bcopy(&ip6->ip6_dst, &key[0], sizeof(struct in6_addr)); bcopy(&ip6->ip6_src, &key[4], sizeof(struct in6_addr)); key[8] = (dport << 16) | sport; fibnum |= proto << 16; fle = flowtable_lookup_common(&V_ip6_ft, key, 9 * sizeof(uint32_t), fibnum); #else /* !FLOWTABLE_HASH_ALL */ bcopy(&ip6->ip6_dst, &key[0], sizeof(struct in6_addr)); fle = flowtable_lookup_common(&V_ip6_ft, key, sizeof(struct in6_addr), fibnum); #endif /* FLOWTABLE_HASH_ALL */ if (fle == NULL) return (NULL); sin6 = (struct sockaddr_in6 *)&ro->ro_dst; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(struct in6_addr)); return (fle); } #endif /* INET6 */ static bitstr_t * flowtable_mask(struct flowtable *ft) { /* * flowtable_free_stale() calls w/o critical section, but * with sched_bind(). Since pointer is stable throughout * ft lifetime, it is safe, otherwise... * * CRITICAL_ASSERT(curthread); */ return (*(bitstr_t **)zpcpu_get(ft->ft_masks)); } static struct flist * flowtable_list(struct flowtable *ft, uint32_t hash) { CRITICAL_ASSERT(curthread); return (zpcpu_get(ft->ft_table[hash % ft->ft_size])); } static int flow_stale(struct flowtable *ft, struct flentry *fle, int maxidle) { if (((fle->f_rt->rt_flags & RTF_UP) == 0) || (fle->f_rt->rt_ifp == NULL) || !RT_LINK_IS_UP(fle->f_rt->rt_ifp) || (fle->f_lle->la_flags & LLE_VALID) == 0) return (1); if (time_uptime - fle->f_uptime > maxidle) return (1); #ifdef FLOWTABLE_HASH_ALL if (fle->f_flags & FL_STALE) return (1); #endif return (0); } static int flow_full(void) { int count, max; count = uma_zone_get_cur(flow_zone); max = uma_zone_get_max(flow_zone); return (count > (max - (max >> 3))); } static int flow_matches(struct flentry *fle, uint32_t *key, int keylen, uint32_t fibnum) { #ifdef FLOWTABLE_HASH_ALL uint8_t proto; proto = (fibnum >> 16) & 0xff; fibnum &= 0xffff; #endif CRITICAL_ASSERT(curthread); /* Microoptimization for IPv4: don't use bcmp(). */ if (((keylen == sizeof(uint32_t) && (fle->f_key[0] == key[0])) || (bcmp(fle->f_key, key, keylen) == 0)) && fibnum == fle->f_fibnum && #ifdef FLOWTABLE_HASH_ALL proto == fle->f_proto && #endif (fle->f_rt->rt_flags & RTF_UP) && fle->f_rt->rt_ifp != NULL && (fle->f_lle->la_flags & LLE_VALID)) return (1); return (0); } static struct flentry * flowtable_insert(struct flowtable *ft, uint32_t hash, uint32_t *key, int keylen, uint32_t fibnum0) { #ifdef INET6 struct route_in6 sro6; #endif #ifdef INET struct route sro; #endif struct route *ro = NULL; struct rtentry *rt; struct lltable *lt = NULL; struct llentry *lle; struct sockaddr_storage *l3addr; struct ifnet *ifp; struct flist *flist; struct flentry *fle, *iter; bitstr_t *mask; uint16_t fibnum = fibnum0; #ifdef FLOWTABLE_HASH_ALL uint8_t proto; proto = (fibnum0 >> 16) & 0xff; fibnum = fibnum0 & 0xffff; #endif /* * This bit of code ends up locking the * same route 3 times (just like ip_output + ether_output) * - at lookup * - in rt_check when called by arpresolve * - dropping the refcount for the rtentry * * This could be consolidated to one if we wrote a variant * of arpresolve with an rt_check variant that expected to * receive the route locked */ #ifdef INET if (ft == &V_ip4_ft) { struct sockaddr_in *sin; ro = &sro; bzero(&sro.ro_dst, sizeof(sro.ro_dst)); sin = (struct sockaddr_in *)&sro.ro_dst; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr.s_addr = key[0]; } #endif #ifdef INET6 if (ft == &V_ip6_ft) { struct sockaddr_in6 *sin6; ro = (struct route *)&sro6; sin6 = &sro6.ro_dst; bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); bcopy(key, &sin6->sin6_addr, sizeof(struct in6_addr)); } #endif ro->ro_rt = NULL; #ifdef RADIX_MPATH rtalloc_mpath_fib(ro, hash, fibnum); #else rtalloc_ign_fib(ro, 0, fibnum); #endif if (ro->ro_rt == NULL) return (NULL); rt = ro->ro_rt; ifp = rt->rt_ifp; if (ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) { RTFREE(rt); return (NULL); } #ifdef INET if (ft == &V_ip4_ft) lt = LLTABLE(ifp); #endif #ifdef INET6 if (ft == &V_ip6_ft) lt = LLTABLE6(ifp); #endif if (rt->rt_flags & RTF_GATEWAY) l3addr = (struct sockaddr_storage *)rt->rt_gateway; else l3addr = (struct sockaddr_storage *)&ro->ro_dst; lle = llentry_alloc(ifp, lt, l3addr); if (lle == NULL) { RTFREE(rt); return (NULL); } /* Don't insert the entry if the ARP hasn't yet finished resolving. */ if ((lle->la_flags & LLE_VALID) == 0) { RTFREE(rt); LLE_FREE(lle); FLOWSTAT_INC(ft, ft_fail_lle_invalid); return (NULL); } fle = uma_zalloc(flow_zone, M_NOWAIT | M_ZERO); if (fle == NULL) { RTFREE(rt); LLE_FREE(lle); return (NULL); } fle->f_hash = hash; bcopy(key, &fle->f_key, keylen); fle->f_rt = rt; fle->f_lle = lle; fle->f_fibnum = fibnum; fle->f_uptime = time_uptime; #ifdef FLOWTABLE_HASH_ALL fle->f_proto = proto; fle->f_flags = fibnum0 >> 24; #endif critical_enter(); mask = flowtable_mask(ft); flist = flowtable_list(ft, hash); if (SLIST_EMPTY(flist)) { bit_set(mask, (hash % ft->ft_size)); SLIST_INSERT_HEAD(flist, fle, f_next); goto skip; } /* * find end of list and make sure that we were not * preempted by another thread handling this flow */ SLIST_FOREACH(iter, flist, f_next) { KASSERT(iter->f_hash % ft->ft_size == hash % ft->ft_size, ("%s: wrong hash", __func__)); if (flow_matches(iter, key, keylen, fibnum)) { /* * We probably migrated to an other CPU after * lookup in flowtable_lookup_common() failed. * It appeared that this CPU already has flow * entry. */ iter->f_uptime = time_uptime; #ifdef FLOWTABLE_HASH_ALL iter->f_flags |= fibnum >> 24; #endif critical_exit(); FLOWSTAT_INC(ft, ft_collisions); uma_zfree(flow_zone, fle); return (iter); } } SLIST_INSERT_HEAD(flist, fle, f_next); skip: critical_exit(); FLOWSTAT_INC(ft, ft_inserts); return (fle); } int flowtable_lookup(sa_family_t sa, struct mbuf *m, struct route *ro) { struct flentry *fle; + struct llentry *lle; if (V_flowtable_enable == 0) return (ENXIO); switch (sa) { #ifdef INET case AF_INET: fle = flowtable_lookup_ipv4(m, ro); break; #endif #ifdef INET6 case AF_INET6: fle = flowtable_lookup_ipv6(m, ro); break; #endif default: panic("%s: sa %d", __func__, sa); } if (fle == NULL) return (EHOSTUNREACH); if (M_HASHTYPE_GET(m) == M_HASHTYPE_NONE) { M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); m->m_pkthdr.flowid = fle->f_hash; } ro->ro_rt = fle->f_rt; - ro->ro_lle = fle->f_lle; ro->ro_flags |= RT_NORTREF; + lle = fle->f_lle; + if (lle != NULL && (lle->la_flags & LLE_VALID)) { + ro->ro_prepend = lle->r_linkdata; + ro->ro_plen = lle->r_hdrlen; + ro->ro_flags |= RT_MAY_LOOP; + if (lle->la_flags & LLE_IFADDR) + ro->ro_flags |= RT_L2_ME; + } return (0); } static struct flentry * flowtable_lookup_common(struct flowtable *ft, uint32_t *key, int keylen, uint32_t fibnum) { struct flist *flist; struct flentry *fle; uint32_t hash; FLOWSTAT_INC(ft, ft_lookups); hash = jenkins_hash32(key, keylen / sizeof(uint32_t), flow_hashjitter); critical_enter(); flist = flowtable_list(ft, hash); SLIST_FOREACH(fle, flist, f_next) { KASSERT(fle->f_hash % ft->ft_size == hash % ft->ft_size, ("%s: wrong hash", __func__)); if (flow_matches(fle, key, keylen, fibnum)) { fle->f_uptime = time_uptime; #ifdef FLOWTABLE_HASH_ALL fle->f_flags |= fibnum >> 24; #endif critical_exit(); FLOWSTAT_INC(ft, ft_hits); return (fle); } } critical_exit(); FLOWSTAT_INC(ft, ft_misses); return (flowtable_insert(ft, hash, key, keylen, fibnum)); } /* * used by the bit_alloc macro */ #define calloc(count, size) malloc((count)*(size), M_FTABLE, M_WAITOK | M_ZERO) static void flowtable_alloc(struct flowtable *ft) { ft->ft_table = malloc(ft->ft_size * sizeof(struct flist), M_FTABLE, M_WAITOK); for (int i = 0; i < ft->ft_size; i++) ft->ft_table[i] = uma_zalloc(pcpu_zone_ptr, M_WAITOK | M_ZERO); ft->ft_masks = uma_zalloc(pcpu_zone_ptr, M_WAITOK); for (int i = 0; i < mp_ncpus; i++) { bitstr_t **b; b = zpcpu_get_cpu(ft->ft_masks, i); *b = bit_alloc(ft->ft_size); } ft->ft_tmpmask = bit_alloc(ft->ft_size); } #undef calloc static void flowtable_free_stale(struct flowtable *ft, struct rtentry *rt, int maxidle) { struct flist *flist, freelist; struct flentry *fle, *fle1, *fleprev; bitstr_t *mask, *tmpmask; int curbit, tmpsize; SLIST_INIT(&freelist); mask = flowtable_mask(ft); tmpmask = ft->ft_tmpmask; tmpsize = ft->ft_size; memcpy(tmpmask, mask, ft->ft_size/8); curbit = 0; fleprev = NULL; /* pacify gcc */ /* * XXX Note to self, bit_ffs operates at the byte level * and thus adds gratuitous overhead */ bit_ffs(tmpmask, ft->ft_size, &curbit); while (curbit != -1) { if (curbit >= ft->ft_size || curbit < -1) { log(LOG_ALERT, "warning: bad curbit value %d \n", curbit); break; } FLOWSTAT_INC(ft, ft_free_checks); critical_enter(); flist = flowtable_list(ft, curbit); #ifdef DIAGNOSTIC if (SLIST_EMPTY(flist) && curbit > 0) { log(LOG_ALERT, "warning bit=%d set, but no fle found\n", curbit); } #endif SLIST_FOREACH_SAFE(fle, flist, f_next, fle1) { if (rt != NULL && fle->f_rt != rt) { fleprev = fle; continue; } if (!flow_stale(ft, fle, maxidle)) { fleprev = fle; continue; } if (fle == SLIST_FIRST(flist)) SLIST_REMOVE_HEAD(flist, f_next); else SLIST_REMOVE_AFTER(fleprev, f_next); SLIST_INSERT_HEAD(&freelist, fle, f_next); } if (SLIST_EMPTY(flist)) bit_clear(mask, curbit); critical_exit(); bit_clear(tmpmask, curbit); bit_ffs(tmpmask, tmpsize, &curbit); } SLIST_FOREACH_SAFE(fle, &freelist, f_next, fle1) { FLOWSTAT_INC(ft, ft_frees); if (fle->f_rt != NULL) RTFREE(fle->f_rt); if (fle->f_lle != NULL) LLE_FREE(fle->f_lle); uma_zfree(flow_zone, fle); } } static void flowtable_clean_vnet(struct flowtable *ft, struct rtentry *rt, int maxidle) { int i; CPU_FOREACH(i) { if (smp_started == 1) { thread_lock(curthread); sched_bind(curthread, i); thread_unlock(curthread); } flowtable_free_stale(ft, rt, maxidle); if (smp_started == 1) { thread_lock(curthread); sched_unbind(curthread); thread_unlock(curthread); } } } void flowtable_route_flush(sa_family_t sa, struct rtentry *rt) { struct flowtable *ft; switch (sa) { #ifdef INET case AF_INET: ft = &V_ip4_ft; break; #endif #ifdef INET6 case AF_INET6: ft = &V_ip6_ft; break; #endif default: panic("%s: sa %d", __func__, sa); } flowtable_clean_vnet(ft, rt, 0); } static void flowtable_cleaner(void) { VNET_ITERATOR_DECL(vnet_iter); struct thread *td; if (bootverbose) log(LOG_INFO, "flowtable cleaner started\n"); td = curthread; while (1) { uint32_t flowclean_freq, maxidle; /* * The maximum idle time, as well as frequency are arbitrary. */ if (flow_full()) maxidle = 5; else maxidle = 30; VNET_LIST_RLOCK(); VNET_FOREACH(vnet_iter) { CURVNET_SET(vnet_iter); #ifdef INET flowtable_clean_vnet(&V_ip4_ft, NULL, maxidle); #endif #ifdef INET6 flowtable_clean_vnet(&V_ip6_ft, NULL, maxidle); #endif CURVNET_RESTORE(); } VNET_LIST_RUNLOCK(); if (flow_full()) flowclean_freq = 4*hz; else flowclean_freq = 20*hz; mtx_lock(&flowclean_lock); thread_lock(td); sched_prio(td, PPAUSE); thread_unlock(td); flowclean_cycles++; cv_broadcast(&flowclean_f_cv); cv_timedwait(&flowclean_c_cv, &flowclean_lock, flowclean_freq); mtx_unlock(&flowclean_lock); } } static void flowtable_flush(void *unused __unused) { uint64_t start; mtx_lock(&flowclean_lock); start = flowclean_cycles; while (start == flowclean_cycles) { cv_broadcast(&flowclean_c_cv); cv_wait(&flowclean_f_cv, &flowclean_lock); } mtx_unlock(&flowclean_lock); } static struct kproc_desc flow_kp = { "flowcleaner", flowtable_cleaner, &flowcleanerproc }; SYSINIT(flowcleaner, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, &flow_kp); static int flowtable_get_size(char *name) { int size; if (TUNABLE_INT_FETCH(name, &size)) { if (size < 256) size = 256; if (!powerof2(size)) { printf("%s must be power of 2\n", name); size = 2048; } } else { /* * round up to the next power of 2 */ size = 1 << fls((1024 + maxusers * 64) - 1); } return (size); } static void flowtable_init(const void *unused __unused) { flow_hashjitter = arc4random(); flow_zone = uma_zcreate("flows", sizeof(struct flentry), NULL, NULL, NULL, NULL, (64-1), UMA_ZONE_MAXBUCKET); uma_zone_set_max(flow_zone, 1024 + maxusers * 64 * mp_ncpus); cv_init(&flowclean_c_cv, "c_flowcleanwait"); cv_init(&flowclean_f_cv, "f_flowcleanwait"); mtx_init(&flowclean_lock, "flowclean lock", NULL, MTX_DEF); EVENTHANDLER_REGISTER(ifnet_departure_event, flowtable_flush, NULL, EVENTHANDLER_PRI_ANY); } SYSINIT(flowtable_init, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST, flowtable_init, NULL); #ifdef INET static SYSCTL_NODE(_net_flowtable, OID_AUTO, ip4, CTLFLAG_RD, NULL, "Flowtable for IPv4"); static VNET_PCPUSTAT_DEFINE(struct flowtable_stat, ip4_ftstat); VNET_PCPUSTAT_SYSINIT(ip4_ftstat); VNET_PCPUSTAT_SYSUNINIT(ip4_ftstat); SYSCTL_VNET_PCPUSTAT(_net_flowtable_ip4, OID_AUTO, stat, struct flowtable_stat, ip4_ftstat, "Flowtable statistics for IPv4 " "(struct flowtable_stat, net/flowtable.h)"); static void flowtable_init_vnet_v4(const void *unused __unused) { V_ip4_ft.ft_size = flowtable_get_size("net.flowtable.ip4.size"); V_ip4_ft.ft_stat = VNET(ip4_ftstat); flowtable_alloc(&V_ip4_ft); } VNET_SYSINIT(ft_vnet_v4, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, flowtable_init_vnet_v4, NULL); #endif /* INET */ #ifdef INET6 static SYSCTL_NODE(_net_flowtable, OID_AUTO, ip6, CTLFLAG_RD, NULL, "Flowtable for IPv6"); static VNET_PCPUSTAT_DEFINE(struct flowtable_stat, ip6_ftstat); VNET_PCPUSTAT_SYSINIT(ip6_ftstat); VNET_PCPUSTAT_SYSUNINIT(ip6_ftstat); SYSCTL_VNET_PCPUSTAT(_net_flowtable_ip6, OID_AUTO, stat, struct flowtable_stat, ip6_ftstat, "Flowtable statistics for IPv6 " "(struct flowtable_stat, net/flowtable.h)"); static void flowtable_init_vnet_v6(const void *unused __unused) { V_ip6_ft.ft_size = flowtable_get_size("net.flowtable.ip6.size"); V_ip6_ft.ft_stat = VNET(ip6_ftstat); flowtable_alloc(&V_ip6_ft); } VNET_SYSINIT(flowtable_init_vnet_v6, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, flowtable_init_vnet_v6, NULL); #endif /* INET6 */ #ifdef DDB static bitstr_t * flowtable_mask_pcpu(struct flowtable *ft, int cpuid) { return (zpcpu_get_cpu(*ft->ft_masks, cpuid)); } static struct flist * flowtable_list_pcpu(struct flowtable *ft, uint32_t hash, int cpuid) { return (zpcpu_get_cpu(&ft->ft_table[hash % ft->ft_size], cpuid)); } static void flow_show(struct flowtable *ft, struct flentry *fle) { int idle_time; int rt_valid, ifp_valid; volatile struct rtentry *rt; struct ifnet *ifp = NULL; uint32_t *hashkey = fle->f_key; idle_time = (int)(time_uptime - fle->f_uptime); rt = fle->f_rt; rt_valid = rt != NULL; if (rt_valid) ifp = rt->rt_ifp; ifp_valid = ifp != NULL; #ifdef INET if (ft == &V_ip4_ft) { char daddr[4*sizeof "123"]; #ifdef FLOWTABLE_HASH_ALL char saddr[4*sizeof "123"]; uint16_t sport, dport; #endif inet_ntoa_r(*(struct in_addr *) &hashkey[0], daddr); #ifdef FLOWTABLE_HASH_ALL inet_ntoa_r(*(struct in_addr *) &hashkey[1], saddr); dport = ntohs((uint16_t)(hashkey[2] >> 16)); sport = ntohs((uint16_t)(hashkey[2] & 0xffff)); db_printf("%s:%d->%s:%d", saddr, sport, daddr, dport); #else db_printf("%s ", daddr); #endif } #endif /* INET */ #ifdef INET6 if (ft == &V_ip6_ft) { #ifdef FLOWTABLE_HASH_ALL db_printf("\n\tkey=%08x:%08x:%08x%08x:%08x:%08x%08x:%08x:%08x", hashkey[0], hashkey[1], hashkey[2], hashkey[3], hashkey[4], hashkey[5], hashkey[6], hashkey[7], hashkey[8]); #else db_printf("\n\tkey=%08x:%08x:%08x ", hashkey[0], hashkey[1], hashkey[2]); #endif } #endif /* INET6 */ db_printf("hash=%08x idle_time=%03d" "\n\tfibnum=%02d rt=%p", fle->f_hash, idle_time, fle->f_fibnum, fle->f_rt); #ifdef FLOWTABLE_HASH_ALL if (fle->f_flags & FL_STALE) db_printf(" FL_STALE "); #endif if (rt_valid) { if (rt->rt_flags & RTF_UP) db_printf(" RTF_UP "); } if (ifp_valid) { if (ifp->if_flags & IFF_LOOPBACK) db_printf(" IFF_LOOPBACK "); if (ifp->if_flags & IFF_UP) db_printf(" IFF_UP "); if (ifp->if_flags & IFF_POINTOPOINT) db_printf(" IFF_POINTOPOINT "); } db_printf("\n"); } static void flowtable_show(struct flowtable *ft, int cpuid) { int curbit = 0; bitstr_t *mask, *tmpmask; if (cpuid != -1) db_printf("cpu: %d\n", cpuid); mask = flowtable_mask_pcpu(ft, cpuid); tmpmask = ft->ft_tmpmask; memcpy(tmpmask, mask, ft->ft_size/8); /* * XXX Note to self, bit_ffs operates at the byte level * and thus adds gratuitous overhead */ bit_ffs(tmpmask, ft->ft_size, &curbit); while (curbit != -1) { struct flist *flist; struct flentry *fle; if (curbit >= ft->ft_size || curbit < -1) { db_printf("warning: bad curbit value %d \n", curbit); break; } flist = flowtable_list_pcpu(ft, curbit, cpuid); SLIST_FOREACH(fle, flist, f_next) flow_show(ft, fle); bit_clear(tmpmask, curbit); bit_ffs(tmpmask, ft->ft_size, &curbit); } } static void flowtable_show_vnet(struct flowtable *ft) { int i; CPU_FOREACH(i) flowtable_show(ft, i); } DB_SHOW_COMMAND(flowtables, db_show_flowtables) { VNET_ITERATOR_DECL(vnet_iter); VNET_FOREACH(vnet_iter) { CURVNET_SET(vnet_iter); #ifdef VIMAGE db_printf("vnet %p\n", vnet_iter); #endif #ifdef INET printf("IPv4:\n"); flowtable_show_vnet(&V_ip4_ft); #endif #ifdef INET6 printf("IPv6:\n"); flowtable_show_vnet(&V_ip6_ft); #endif CURVNET_RESTORE(); } } #endif Index: projects/clang380-import/sys/net/if.c =================================================================== --- projects/clang380-import/sys/net/if.c (revision 293015) +++ projects/clang380-import/sys/net/if.c (revision 293016) @@ -1,3955 +1,3996 @@ /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if.c 8.5 (Berkeley) 1/9/95 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_inet.h" #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #ifdef INET #include #endif /* INET */ #ifdef INET6 #include #include #endif /* INET6 */ #endif /* INET || INET6 */ #include #ifdef COMPAT_FREEBSD32 #include #include #endif SYSCTL_NODE(_net, PF_LINK, link, CTLFLAG_RW, 0, "Link layers"); SYSCTL_NODE(_net_link, 0, generic, CTLFLAG_RW, 0, "Generic link-management"); SYSCTL_INT(_net_link, OID_AUTO, ifqmaxlen, CTLFLAG_RDTUN, &ifqmaxlen, 0, "max send queue size"); /* Log link state change events */ static int log_link_state_change = 1; SYSCTL_INT(_net_link, OID_AUTO, log_link_state_change, CTLFLAG_RW, &log_link_state_change, 0, "log interface link state change events"); /* Interface description */ static unsigned int ifdescr_maxlen = 1024; SYSCTL_UINT(_net, OID_AUTO, ifdescr_maxlen, CTLFLAG_RW, &ifdescr_maxlen, 0, "administrative maximum length for interface description"); static MALLOC_DEFINE(M_IFDESCR, "ifdescr", "ifnet descriptions"); /* global sx for non-critical path ifdescr */ static struct sx ifdescr_sx; SX_SYSINIT(ifdescr_sx, &ifdescr_sx, "ifnet descr"); void (*bridge_linkstate_p)(struct ifnet *ifp); void (*ng_ether_link_state_p)(struct ifnet *ifp, int state); void (*lagg_linkstate_p)(struct ifnet *ifp, int state); /* These are external hooks for CARP. */ void (*carp_linkstate_p)(struct ifnet *ifp); void (*carp_demote_adj_p)(int, char *); int (*carp_master_p)(struct ifaddr *); #if defined(INET) || defined(INET6) int (*carp_forus_p)(struct ifnet *ifp, u_char *dhost); int (*carp_output_p)(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa); int (*carp_ioctl_p)(struct ifreq *, u_long, struct thread *); int (*carp_attach_p)(struct ifaddr *, int); void (*carp_detach_p)(struct ifaddr *); #endif #ifdef INET int (*carp_iamatch_p)(struct ifaddr *, uint8_t **); #endif #ifdef INET6 struct ifaddr *(*carp_iamatch6_p)(struct ifnet *ifp, struct in6_addr *taddr6); caddr_t (*carp_macmatch6_p)(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr); #endif struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) = NULL; /* * XXX: Style; these should be sorted alphabetically, and unprototyped * static functions should be prototyped. Currently they are sorted by * declaration order. */ static void if_attachdomain(void *); static void if_attachdomain1(struct ifnet *); static int ifconf(u_long, caddr_t); static void if_freemulti(struct ifmultiaddr *); static void if_grow(void); static void if_input_default(struct ifnet *, struct mbuf *); +static int if_requestencap_default(struct ifnet *, struct if_encap_req *); static void if_route(struct ifnet *, int flag, int fam); static int if_setflag(struct ifnet *, int, int, int *, int); static int if_transmit(struct ifnet *ifp, struct mbuf *m); static void if_unroute(struct ifnet *, int flag, int fam); static void link_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static int ifhwioctl(u_long, struct ifnet *, caddr_t, struct thread *); static int if_delmulti_locked(struct ifnet *, struct ifmultiaddr *, int); static void do_link_state_change(void *, int); static int if_getgroup(struct ifgroupreq *, struct ifnet *); static int if_getgroupmembers(struct ifgroupreq *); static void if_delgroups(struct ifnet *); static void if_attach_internal(struct ifnet *, int, struct if_clone *); static int if_detach_internal(struct ifnet *, int, struct if_clone **); #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. * should be more generalized? */ extern void nd6_setmtu(struct ifnet *); #endif /* ipsec helper hooks */ VNET_DEFINE(struct hhook_head *, ipsec_hhh_in[HHOOK_IPSEC_COUNT]); VNET_DEFINE(struct hhook_head *, ipsec_hhh_out[HHOOK_IPSEC_COUNT]); VNET_DEFINE(int, if_index); int ifqmaxlen = IFQ_MAXLEN; VNET_DEFINE(struct ifnethead, ifnet); /* depend on static init XXX */ VNET_DEFINE(struct ifgrouphead, ifg_head); static VNET_DEFINE(int, if_indexlim) = 8; /* Table of ifnet by index. */ VNET_DEFINE(struct ifnet **, ifindex_table); #define V_if_indexlim VNET(if_indexlim) #define V_ifindex_table VNET(ifindex_table) /* * The global network interface list (V_ifnet) and related state (such as * if_index, if_indexlim, and ifindex_table) are protected by an sxlock and * an rwlock. Either may be acquired shared to stablize the list, but both * must be acquired writable to modify the list. This model allows us to * both stablize the interface list during interrupt thread processing, but * also to stablize it over long-running ioctls, without introducing priority * inversions and deadlocks. */ struct rwlock ifnet_rwlock; RW_SYSINIT_FLAGS(ifnet_rw, &ifnet_rwlock, "ifnet_rw", RW_RECURSE); struct sx ifnet_sxlock; SX_SYSINIT_FLAGS(ifnet_sx, &ifnet_sxlock, "ifnet_sx", SX_RECURSE); /* * The allocation of network interfaces is a rather non-atomic affair; we * need to select an index before we are ready to expose the interface for * use, so will use this pointer value to indicate reservation. */ #define IFNET_HOLD (void *)(uintptr_t)(-1) static if_com_alloc_t *if_com_alloc[256]; static if_com_free_t *if_com_free[256]; static MALLOC_DEFINE(M_IFNET, "ifnet", "interface internals"); MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address"); MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address"); struct ifnet * ifnet_byindex_locked(u_short idx) { if (idx > V_if_index) return (NULL); if (V_ifindex_table[idx] == IFNET_HOLD) return (NULL); return (V_ifindex_table[idx]); } struct ifnet * ifnet_byindex(u_short idx) { struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); ifp = ifnet_byindex_locked(idx); IFNET_RUNLOCK_NOSLEEP(); return (ifp); } struct ifnet * ifnet_byindex_ref(u_short idx) { struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); ifp = ifnet_byindex_locked(idx); if (ifp == NULL || (ifp->if_flags & IFF_DYING)) { IFNET_RUNLOCK_NOSLEEP(); return (NULL); } if_ref(ifp); IFNET_RUNLOCK_NOSLEEP(); return (ifp); } /* * Allocate an ifindex array entry; return 0 on success or an error on * failure. */ static u_short ifindex_alloc(void) { u_short idx; IFNET_WLOCK_ASSERT(); retry: /* * Try to find an empty slot below V_if_index. If we fail, take the * next slot. */ for (idx = 1; idx <= V_if_index; idx++) { if (V_ifindex_table[idx] == NULL) break; } /* Catch if_index overflow. */ if (idx >= V_if_indexlim) { if_grow(); goto retry; } if (idx > V_if_index) V_if_index = idx; return (idx); } static void ifindex_free_locked(u_short idx) { IFNET_WLOCK_ASSERT(); V_ifindex_table[idx] = NULL; while (V_if_index > 0 && V_ifindex_table[V_if_index] == NULL) V_if_index--; } static void ifindex_free(u_short idx) { IFNET_WLOCK(); ifindex_free_locked(idx); IFNET_WUNLOCK(); } static void ifnet_setbyindex_locked(u_short idx, struct ifnet *ifp) { IFNET_WLOCK_ASSERT(); V_ifindex_table[idx] = ifp; } static void ifnet_setbyindex(u_short idx, struct ifnet *ifp) { IFNET_WLOCK(); ifnet_setbyindex_locked(idx, ifp); IFNET_WUNLOCK(); } struct ifaddr * ifaddr_byindex(u_short idx) { struct ifnet *ifp; struct ifaddr *ifa = NULL; IFNET_RLOCK_NOSLEEP(); ifp = ifnet_byindex_locked(idx); if (ifp != NULL && (ifa = ifp->if_addr) != NULL) ifa_ref(ifa); IFNET_RUNLOCK_NOSLEEP(); return (ifa); } /* * Network interface utility routines. * * Routines with ifa_ifwith* names take sockaddr *'s as * parameters. */ static void vnet_if_init(const void *unused __unused) { TAILQ_INIT(&V_ifnet); TAILQ_INIT(&V_ifg_head); IFNET_WLOCK(); if_grow(); /* create initial table */ IFNET_WUNLOCK(); vnet_if_clone_init(); } VNET_SYSINIT(vnet_if_init, SI_SUB_INIT_IF, SI_ORDER_SECOND, vnet_if_init, NULL); #ifdef VIMAGE static void vnet_if_uninit(const void *unused __unused) { VNET_ASSERT(TAILQ_EMPTY(&V_ifnet), ("%s:%d tailq &V_ifnet=%p " "not empty", __func__, __LINE__, &V_ifnet)); VNET_ASSERT(TAILQ_EMPTY(&V_ifg_head), ("%s:%d tailq &V_ifg_head=%p " "not empty", __func__, __LINE__, &V_ifg_head)); free((caddr_t)V_ifindex_table, M_IFNET); } VNET_SYSUNINIT(vnet_if_uninit, SI_SUB_INIT_IF, SI_ORDER_FIRST, vnet_if_uninit, NULL); #endif static void if_grow(void) { int oldlim; u_int n; struct ifnet **e; IFNET_WLOCK_ASSERT(); oldlim = V_if_indexlim; IFNET_WUNLOCK(); n = (oldlim << 1) * sizeof(*e); e = malloc(n, M_IFNET, M_WAITOK | M_ZERO); IFNET_WLOCK(); if (V_if_indexlim != oldlim) { free(e, M_IFNET); return; } if (V_ifindex_table != NULL) { memcpy((caddr_t)e, (caddr_t)V_ifindex_table, n/2); free((caddr_t)V_ifindex_table, M_IFNET); } V_if_indexlim <<= 1; V_ifindex_table = e; } /* * Allocate a struct ifnet and an index for an interface. A layer 2 * common structure will also be allocated if an allocation routine is * registered for the passed type. */ struct ifnet * if_alloc(u_char type) { struct ifnet *ifp; u_short idx; ifp = malloc(sizeof(struct ifnet), M_IFNET, M_WAITOK|M_ZERO); IFNET_WLOCK(); idx = ifindex_alloc(); ifnet_setbyindex_locked(idx, IFNET_HOLD); IFNET_WUNLOCK(); ifp->if_index = idx; ifp->if_type = type; ifp->if_alloctype = type; if (if_com_alloc[type] != NULL) { ifp->if_l2com = if_com_alloc[type](type, ifp); if (ifp->if_l2com == NULL) { free(ifp, M_IFNET); ifindex_free(idx); return (NULL); } } IF_ADDR_LOCK_INIT(ifp); TASK_INIT(&ifp->if_linktask, 0, do_link_state_change, ifp); ifp->if_afdata_initialized = 0; IF_AFDATA_LOCK_INIT(ifp); TAILQ_INIT(&ifp->if_addrhead); TAILQ_INIT(&ifp->if_multiaddrs); TAILQ_INIT(&ifp->if_groups); #ifdef MAC mac_ifnet_init(ifp); #endif ifq_init(&ifp->if_snd, ifp); refcount_init(&ifp->if_refcount, 1); /* Index reference. */ for (int i = 0; i < IFCOUNTERS; i++) ifp->if_counters[i] = counter_u64_alloc(M_WAITOK); ifp->if_get_counter = if_get_counter_default; ifnet_setbyindex(ifp->if_index, ifp); return (ifp); } /* * Do the actual work of freeing a struct ifnet, and layer 2 common * structure. This call is made when the last reference to an * interface is released. */ static void if_free_internal(struct ifnet *ifp) { KASSERT((ifp->if_flags & IFF_DYING), ("if_free_internal: interface not dying")); if (if_com_free[ifp->if_alloctype] != NULL) if_com_free[ifp->if_alloctype](ifp->if_l2com, ifp->if_alloctype); #ifdef MAC mac_ifnet_destroy(ifp); #endif /* MAC */ if (ifp->if_description != NULL) free(ifp->if_description, M_IFDESCR); IF_AFDATA_DESTROY(ifp); IF_ADDR_LOCK_DESTROY(ifp); ifq_delete(&ifp->if_snd); for (int i = 0; i < IFCOUNTERS; i++) counter_u64_free(ifp->if_counters[i]); free(ifp, M_IFNET); } /* * Deregister an interface and free the associated storage. */ void if_free(struct ifnet *ifp) { ifp->if_flags |= IFF_DYING; /* XXX: Locking */ CURVNET_SET_QUIET(ifp->if_vnet); IFNET_WLOCK(); KASSERT(ifp == ifnet_byindex_locked(ifp->if_index), ("%s: freeing unallocated ifnet", ifp->if_xname)); ifindex_free_locked(ifp->if_index); IFNET_WUNLOCK(); if (refcount_release(&ifp->if_refcount)) if_free_internal(ifp); CURVNET_RESTORE(); } /* * Interfaces to keep an ifnet type-stable despite the possibility of the * driver calling if_free(). If there are additional references, we defer * freeing the underlying data structure. */ void if_ref(struct ifnet *ifp) { /* We don't assert the ifnet list lock here, but arguably should. */ refcount_acquire(&ifp->if_refcount); } void if_rele(struct ifnet *ifp) { if (!refcount_release(&ifp->if_refcount)) return; if_free_internal(ifp); } void ifq_init(struct ifaltq *ifq, struct ifnet *ifp) { mtx_init(&ifq->ifq_mtx, ifp->if_xname, "if send queue", MTX_DEF); if (ifq->ifq_maxlen == 0) ifq->ifq_maxlen = ifqmaxlen; ifq->altq_type = 0; ifq->altq_disc = NULL; ifq->altq_flags &= ALTQF_CANTCHANGE; ifq->altq_tbr = NULL; ifq->altq_ifp = ifp; } void ifq_delete(struct ifaltq *ifq) { mtx_destroy(&ifq->ifq_mtx); } /* * Perform generic interface initalization tasks and attach the interface * to the list of "active" interfaces. If vmove flag is set on entry * to if_attach_internal(), perform only a limited subset of initialization * tasks, given that we are moving from one vnet to another an ifnet which * has already been fully initialized. * * Note that if_detach_internal() removes group membership unconditionally * even when vmove flag is set, and if_attach_internal() adds only IFG_ALL. * Thus, when if_vmove() is applied to a cloned interface, group membership * is lost while a cloned one always joins a group whose name is * ifc->ifc_name. To recover this after if_detach_internal() and * if_attach_internal(), the cloner should be specified to * if_attach_internal() via ifc. If it is non-NULL, if_attach_internal() * attempts to join a group whose name is ifc->ifc_name. * * XXX: * - The decision to return void and thus require this function to * succeed is questionable. * - We should probably do more sanity checking. For instance we don't * do anything to insure if_xname is unique or non-empty. */ void if_attach(struct ifnet *ifp) { if_attach_internal(ifp, 0, NULL); } /* * Compute the least common TSO limit. */ void if_hw_tsomax_common(if_t ifp, struct ifnet_hw_tsomax *pmax) { /* * 1) If there is no limit currently, take the limit from * the network adapter. * * 2) If the network adapter has a limit below the current * limit, apply it. */ if (pmax->tsomaxbytes == 0 || (ifp->if_hw_tsomax != 0 && ifp->if_hw_tsomax < pmax->tsomaxbytes)) { pmax->tsomaxbytes = ifp->if_hw_tsomax; } if (pmax->tsomaxsegcount == 0 || (ifp->if_hw_tsomaxsegcount != 0 && ifp->if_hw_tsomaxsegcount < pmax->tsomaxsegcount)) { pmax->tsomaxsegcount = ifp->if_hw_tsomaxsegcount; } if (pmax->tsomaxsegsize == 0 || (ifp->if_hw_tsomaxsegsize != 0 && ifp->if_hw_tsomaxsegsize < pmax->tsomaxsegsize)) { pmax->tsomaxsegsize = ifp->if_hw_tsomaxsegsize; } } /* * Update TSO limit of a network adapter. * * Returns zero if no change. Else non-zero. */ int if_hw_tsomax_update(if_t ifp, struct ifnet_hw_tsomax *pmax) { int retval = 0; if (ifp->if_hw_tsomax != pmax->tsomaxbytes) { ifp->if_hw_tsomax = pmax->tsomaxbytes; retval++; } if (ifp->if_hw_tsomaxsegsize != pmax->tsomaxsegsize) { ifp->if_hw_tsomaxsegsize = pmax->tsomaxsegsize; retval++; } if (ifp->if_hw_tsomaxsegcount != pmax->tsomaxsegcount) { ifp->if_hw_tsomaxsegcount = pmax->tsomaxsegcount; retval++; } return (retval); } static void if_attach_internal(struct ifnet *ifp, int vmove, struct if_clone *ifc) { unsigned socksize, ifasize; int namelen, masklen; struct sockaddr_dl *sdl; struct ifaddr *ifa; if (ifp->if_index == 0 || ifp != ifnet_byindex(ifp->if_index)) panic ("%s: BUG: if_attach called without if_alloc'd input()\n", ifp->if_xname); #ifdef VIMAGE ifp->if_vnet = curvnet; if (ifp->if_home_vnet == NULL) ifp->if_home_vnet = curvnet; #endif if_addgroup(ifp, IFG_ALL); /* Restore group membership for cloned interfaces. */ if (vmove && ifc != NULL) if_clone_addgroup(ifp, ifc); getmicrotime(&ifp->if_lastchange); ifp->if_epoch = time_uptime; KASSERT((ifp->if_transmit == NULL && ifp->if_qflush == NULL) || (ifp->if_transmit != NULL && ifp->if_qflush != NULL), ("transmit and qflush must both either be set or both be NULL")); if (ifp->if_transmit == NULL) { ifp->if_transmit = if_transmit; ifp->if_qflush = if_qflush; } if (ifp->if_input == NULL) ifp->if_input = if_input_default; + if (ifp->if_requestencap == NULL) + ifp->if_requestencap = if_requestencap_default; + if (!vmove) { #ifdef MAC mac_ifnet_create(ifp); #endif /* * Create a Link Level name for this device. */ namelen = strlen(ifp->if_xname); /* * Always save enough space for any possiable name so we * can do a rename in place later. */ masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + IFNAMSIZ; socksize = masklen + ifp->if_addrlen; if (socksize < sizeof(*sdl)) socksize = sizeof(*sdl); socksize = roundup2(socksize, sizeof(long)); ifasize = sizeof(*ifa) + 2 * socksize; ifa = ifa_alloc(ifasize, M_WAITOK); sdl = (struct sockaddr_dl *)(ifa + 1); sdl->sdl_len = socksize; sdl->sdl_family = AF_LINK; bcopy(ifp->if_xname, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; ifp->if_addr = ifa; ifa->ifa_ifp = ifp; ifa->ifa_rtrequest = link_rtrequest; ifa->ifa_addr = (struct sockaddr *)sdl; sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); ifa->ifa_netmask = (struct sockaddr *)sdl; sdl->sdl_len = masklen; while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; TAILQ_INSERT_HEAD(&ifp->if_addrhead, ifa, ifa_link); /* Reliably crash if used uninitialized. */ ifp->if_broadcastaddr = NULL; #if defined(INET) || defined(INET6) /* Use defaults for TSO, if nothing is set */ if (ifp->if_hw_tsomax == 0 && ifp->if_hw_tsomaxsegcount == 0 && ifp->if_hw_tsomaxsegsize == 0) { /* * The TSO defaults needs to be such that an * NFS mbuf list of 35 mbufs totalling just * below 64K works and that a chain of mbufs * can be defragged into at most 32 segments: */ ifp->if_hw_tsomax = min(IP_MAXPACKET, (32 * MCLBYTES) - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN)); ifp->if_hw_tsomaxsegcount = 35; ifp->if_hw_tsomaxsegsize = 2048; /* 2K */ /* XXX some drivers set IFCAP_TSO after ethernet attach */ if (ifp->if_capabilities & IFCAP_TSO) { if_printf(ifp, "Using defaults for TSO: %u/%u/%u\n", ifp->if_hw_tsomax, ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize); } } #endif } #ifdef VIMAGE else { /* * Update the interface index in the link layer address * of the interface. */ for (ifa = ifp->if_addr; ifa != NULL; ifa = TAILQ_NEXT(ifa, ifa_link)) { if (ifa->ifa_addr->sa_family == AF_LINK) { sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_index = ifp->if_index; } } } #endif IFNET_WLOCK(); TAILQ_INSERT_TAIL(&V_ifnet, ifp, if_link); #ifdef VIMAGE curvnet->vnet_ifcnt++; #endif IFNET_WUNLOCK(); if (domain_init_status >= 2) if_attachdomain1(ifp); EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, "ATTACH", NULL); /* Announce the interface. */ rt_ifannouncemsg(ifp, IFAN_ARRIVAL); } static void if_attachdomain(void *dummy) { struct ifnet *ifp; TAILQ_FOREACH(ifp, &V_ifnet, if_link) if_attachdomain1(ifp); } SYSINIT(domainifattach, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_SECOND, if_attachdomain, NULL); static void if_attachdomain1(struct ifnet *ifp) { struct domain *dp; /* * Since dp->dom_ifattach calls malloc() with M_WAITOK, we * cannot lock ifp->if_afdata initialization, entirely. */ if (IF_AFDATA_TRYLOCK(ifp) == 0) return; if (ifp->if_afdata_initialized >= domain_init_status) { IF_AFDATA_UNLOCK(ifp); log(LOG_WARNING, "%s called more than once on %s\n", __func__, ifp->if_xname); return; } ifp->if_afdata_initialized = domain_init_status; IF_AFDATA_UNLOCK(ifp); /* address family dependent data region */ bzero(ifp->if_afdata, sizeof(ifp->if_afdata)); for (dp = domains; dp; dp = dp->dom_next) { if (dp->dom_ifattach) ifp->if_afdata[dp->dom_family] = (*dp->dom_ifattach)(ifp); } } /* * Remove any unicast or broadcast network addresses from an interface. */ void if_purgeaddrs(struct ifnet *ifp) { struct ifaddr *ifa, *next; TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrhead, ifa_link, next) { if (ifa->ifa_addr->sa_family == AF_LINK) continue; #ifdef INET /* XXX: Ugly!! ad hoc just for INET */ if (ifa->ifa_addr->sa_family == AF_INET) { struct ifaliasreq ifr; bzero(&ifr, sizeof(ifr)); ifr.ifra_addr = *ifa->ifa_addr; if (ifa->ifa_dstaddr) ifr.ifra_broadaddr = *ifa->ifa_dstaddr; if (in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, NULL) == 0) continue; } #endif /* INET */ #ifdef INET6 if (ifa->ifa_addr->sa_family == AF_INET6) { in6_purgeaddr(ifa); /* ifp_addrhead is already updated */ continue; } #endif /* INET6 */ TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); ifa_free(ifa); } } /* * Remove any multicast network addresses from an interface when an ifnet * is going away. */ static void if_purgemaddrs(struct ifnet *ifp) { struct ifmultiaddr *ifma; struct ifmultiaddr *next; IF_ADDR_WLOCK(ifp); TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) if_delmulti_locked(ifp, ifma, 1); IF_ADDR_WUNLOCK(ifp); } /* * Detach an interface, removing it from the list of "active" interfaces. * If vmove flag is set on entry to if_detach_internal(), perform only a * limited subset of cleanup tasks, given that we are moving an ifnet from * one vnet to another, where it must be fully operational. * * XXXRW: There are some significant questions about event ordering, and * how to prevent things from starting to use the interface during detach. */ void if_detach(struct ifnet *ifp) { CURVNET_SET_QUIET(ifp->if_vnet); if_detach_internal(ifp, 0, NULL); CURVNET_RESTORE(); } static int if_detach_internal(struct ifnet *ifp, int vmove, struct if_clone **ifcp) { struct ifaddr *ifa; int i; struct domain *dp; struct ifnet *iter; int found = 0; IFNET_WLOCK(); TAILQ_FOREACH(iter, &V_ifnet, if_link) if (iter == ifp) { TAILQ_REMOVE(&V_ifnet, ifp, if_link); found = 1; break; } #ifdef VIMAGE if (found) curvnet->vnet_ifcnt--; #endif IFNET_WUNLOCK(); if (!found) { /* * While we would want to panic here, we cannot * guarantee that the interface is indeed still on * the list given we don't hold locks all the way. */ return (ENOENT); #if 0 if (vmove) panic("%s: ifp=%p not on the ifnet tailq %p", __func__, ifp, &V_ifnet); else return; /* XXX this should panic as well? */ #endif } /* Check if this is a cloned interface or not. */ if (vmove && ifcp != NULL) *ifcp = if_clone_findifc(ifp); /* * Remove/wait for pending events. */ taskqueue_drain(taskqueue_swi, &ifp->if_linktask); /* * Remove routes and flush queues. */ if_down(ifp); #ifdef ALTQ if (ALTQ_IS_ENABLED(&ifp->if_snd)) altq_disable(&ifp->if_snd); if (ALTQ_IS_ATTACHED(&ifp->if_snd)) altq_detach(&ifp->if_snd); #endif if_purgeaddrs(ifp); #ifdef INET in_ifdetach(ifp); #endif #ifdef INET6 /* * Remove all IPv6 kernel structs related to ifp. This should be done * before removing routing entries below, since IPv6 interface direct * routes are expected to be removed by the IPv6-specific kernel API. * Otherwise, the kernel will detect some inconsistency and bark it. */ in6_ifdetach(ifp); #endif if_purgemaddrs(ifp); /* Announce that the interface is gone. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, "DETACH", NULL); if (!vmove) { /* * Prevent further calls into the device driver via ifnet. */ if_dead(ifp); /* * Remove link ifaddr pointer and maybe decrement if_index. * Clean up all addresses. */ ifp->if_addr = NULL; /* We can now free link ifaddr. */ if (!TAILQ_EMPTY(&ifp->if_addrhead)) { ifa = TAILQ_FIRST(&ifp->if_addrhead); TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); ifa_free(ifa); } } rt_flushifroutes(ifp); if_delgroups(ifp); /* * We cannot hold the lock over dom_ifdetach calls as they might * sleep, for example trying to drain a callout, thus open up the * theoretical race with re-attaching. */ IF_AFDATA_LOCK(ifp); i = ifp->if_afdata_initialized; ifp->if_afdata_initialized = 0; IF_AFDATA_UNLOCK(ifp); for (dp = domains; i > 0 && dp; dp = dp->dom_next) { if (dp->dom_ifdetach && ifp->if_afdata[dp->dom_family]) (*dp->dom_ifdetach)(ifp, ifp->if_afdata[dp->dom_family]); } return (0); } #ifdef VIMAGE /* * if_vmove() performs a limited version of if_detach() in current * vnet and if_attach()es the ifnet to the vnet specified as 2nd arg. * An attempt is made to shrink if_index in current vnet, find an * unused if_index in target vnet and calls if_grow() if necessary, * and finally find an unused if_xname for the target vnet. */ void if_vmove(struct ifnet *ifp, struct vnet *new_vnet) { struct if_clone *ifc; int rc; /* * Detach from current vnet, but preserve LLADDR info, do not * mark as dead etc. so that the ifnet can be reattached later. * If we cannot find it, we lost the race to someone else. */ rc = if_detach_internal(ifp, 1, &ifc); if (rc != 0) return; /* * Unlink the ifnet from ifindex_table[] in current vnet, and shrink * the if_index for that vnet if possible. * * NOTE: IFNET_WLOCK/IFNET_WUNLOCK() are assumed to be unvirtualized, * or we'd lock on one vnet and unlock on another. */ IFNET_WLOCK(); ifindex_free_locked(ifp->if_index); IFNET_WUNLOCK(); /* * Perform interface-specific reassignment tasks, if provided by * the driver. */ if (ifp->if_reassign != NULL) ifp->if_reassign(ifp, new_vnet, NULL); /* * Switch to the context of the target vnet. */ CURVNET_SET_QUIET(new_vnet); IFNET_WLOCK(); ifp->if_index = ifindex_alloc(); ifnet_setbyindex_locked(ifp->if_index, ifp); IFNET_WUNLOCK(); if_attach_internal(ifp, 1, ifc); CURVNET_RESTORE(); } /* * Move an ifnet to or from another child prison/vnet, specified by the jail id. */ static int if_vmove_loan(struct thread *td, struct ifnet *ifp, char *ifname, int jid) { struct prison *pr; struct ifnet *difp; /* Try to find the prison within our visibility. */ sx_slock(&allprison_lock); pr = prison_find_child(td->td_ucred->cr_prison, jid); sx_sunlock(&allprison_lock); if (pr == NULL) return (ENXIO); prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); /* Do not try to move the iface from and to the same prison. */ if (pr->pr_vnet == ifp->if_vnet) { prison_free(pr); return (EEXIST); } /* Make sure the named iface does not exists in the dst. prison/vnet. */ /* XXX Lock interfaces to avoid races. */ CURVNET_SET_QUIET(pr->pr_vnet); difp = ifunit(ifname); CURVNET_RESTORE(); if (difp != NULL) { prison_free(pr); return (EEXIST); } /* Move the interface into the child jail/vnet. */ if_vmove(ifp, pr->pr_vnet); /* Report the new if_xname back to the userland. */ sprintf(ifname, "%s", ifp->if_xname); prison_free(pr); return (0); } static int if_vmove_reclaim(struct thread *td, char *ifname, int jid) { struct prison *pr; struct vnet *vnet_dst; struct ifnet *ifp; /* Try to find the prison within our visibility. */ sx_slock(&allprison_lock); pr = prison_find_child(td->td_ucred->cr_prison, jid); sx_sunlock(&allprison_lock); if (pr == NULL) return (ENXIO); prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); /* Make sure the named iface exists in the source prison/vnet. */ CURVNET_SET(pr->pr_vnet); ifp = ifunit(ifname); /* XXX Lock to avoid races. */ if (ifp == NULL) { CURVNET_RESTORE(); prison_free(pr); return (ENXIO); } /* Do not try to move the iface from and to the same prison. */ vnet_dst = TD_TO_VNET(td); if (vnet_dst == ifp->if_vnet) { CURVNET_RESTORE(); prison_free(pr); return (EEXIST); } /* Get interface back from child jail/vnet. */ if_vmove(ifp, vnet_dst); CURVNET_RESTORE(); /* Report the new if_xname back to the userland. */ sprintf(ifname, "%s", ifp->if_xname); prison_free(pr); return (0); } #endif /* VIMAGE */ /* * Add a group to an interface */ int if_addgroup(struct ifnet *ifp, const char *groupname) { struct ifg_list *ifgl; struct ifg_group *ifg = NULL; struct ifg_member *ifgm; int new = 0; if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' && groupname[strlen(groupname) - 1] <= '9') return (EINVAL); IFNET_WLOCK(); TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) if (!strcmp(ifgl->ifgl_group->ifg_group, groupname)) { IFNET_WUNLOCK(); return (EEXIST); } if ((ifgl = (struct ifg_list *)malloc(sizeof(struct ifg_list), M_TEMP, M_NOWAIT)) == NULL) { IFNET_WUNLOCK(); return (ENOMEM); } if ((ifgm = (struct ifg_member *)malloc(sizeof(struct ifg_member), M_TEMP, M_NOWAIT)) == NULL) { free(ifgl, M_TEMP); IFNET_WUNLOCK(); return (ENOMEM); } TAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) if (!strcmp(ifg->ifg_group, groupname)) break; if (ifg == NULL) { if ((ifg = (struct ifg_group *)malloc(sizeof(struct ifg_group), M_TEMP, M_NOWAIT)) == NULL) { free(ifgl, M_TEMP); free(ifgm, M_TEMP); IFNET_WUNLOCK(); return (ENOMEM); } strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group)); ifg->ifg_refcnt = 0; TAILQ_INIT(&ifg->ifg_members); TAILQ_INSERT_TAIL(&V_ifg_head, ifg, ifg_next); new = 1; } ifg->ifg_refcnt++; ifgl->ifgl_group = ifg; ifgm->ifgm_ifp = ifp; IF_ADDR_WLOCK(ifp); TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next); TAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next); IF_ADDR_WUNLOCK(ifp); IFNET_WUNLOCK(); if (new) EVENTHANDLER_INVOKE(group_attach_event, ifg); EVENTHANDLER_INVOKE(group_change_event, groupname); return (0); } /* * Remove a group from an interface */ int if_delgroup(struct ifnet *ifp, const char *groupname) { struct ifg_list *ifgl; struct ifg_member *ifgm; IFNET_WLOCK(); TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) if (!strcmp(ifgl->ifgl_group->ifg_group, groupname)) break; if (ifgl == NULL) { IFNET_WUNLOCK(); return (ENOENT); } IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next); IF_ADDR_WUNLOCK(ifp); TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next) if (ifgm->ifgm_ifp == ifp) break; if (ifgm != NULL) { TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next); free(ifgm, M_TEMP); } if (--ifgl->ifgl_group->ifg_refcnt == 0) { TAILQ_REMOVE(&V_ifg_head, ifgl->ifgl_group, ifg_next); IFNET_WUNLOCK(); EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); free(ifgl->ifgl_group, M_TEMP); } else IFNET_WUNLOCK(); free(ifgl, M_TEMP); EVENTHANDLER_INVOKE(group_change_event, groupname); return (0); } /* * Remove an interface from all groups */ static void if_delgroups(struct ifnet *ifp) { struct ifg_list *ifgl; struct ifg_member *ifgm; char groupname[IFNAMSIZ]; IFNET_WLOCK(); while (!TAILQ_EMPTY(&ifp->if_groups)) { ifgl = TAILQ_FIRST(&ifp->if_groups); strlcpy(groupname, ifgl->ifgl_group->ifg_group, IFNAMSIZ); IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next); IF_ADDR_WUNLOCK(ifp); TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next) if (ifgm->ifgm_ifp == ifp) break; if (ifgm != NULL) { TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next); free(ifgm, M_TEMP); } if (--ifgl->ifgl_group->ifg_refcnt == 0) { TAILQ_REMOVE(&V_ifg_head, ifgl->ifgl_group, ifg_next); IFNET_WUNLOCK(); EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); free(ifgl->ifgl_group, M_TEMP); } else IFNET_WUNLOCK(); free(ifgl, M_TEMP); EVENTHANDLER_INVOKE(group_change_event, groupname); IFNET_WLOCK(); } IFNET_WUNLOCK(); } /* * Stores all groups from an interface in memory pointed * to by data */ static int if_getgroup(struct ifgroupreq *data, struct ifnet *ifp) { int len, error; struct ifg_list *ifgl; struct ifg_req ifgrq, *ifgp; struct ifgroupreq *ifgr = data; if (ifgr->ifgr_len == 0) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) ifgr->ifgr_len += sizeof(struct ifg_req); IF_ADDR_RUNLOCK(ifp); return (0); } len = ifgr->ifgr_len; ifgp = ifgr->ifgr_groups; /* XXX: wire */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) { if (len < sizeof(ifgrq)) { IF_ADDR_RUNLOCK(ifp); return (EINVAL); } bzero(&ifgrq, sizeof ifgrq); strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group, sizeof(ifgrq.ifgrq_group)); if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) { IF_ADDR_RUNLOCK(ifp); return (error); } len -= sizeof(ifgrq); ifgp++; } IF_ADDR_RUNLOCK(ifp); return (0); } /* * Stores all members of a group in memory pointed to by data */ static int if_getgroupmembers(struct ifgroupreq *data) { struct ifgroupreq *ifgr = data; struct ifg_group *ifg; struct ifg_member *ifgm; struct ifg_req ifgrq, *ifgp; int len, error; IFNET_RLOCK(); TAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) if (!strcmp(ifg->ifg_group, ifgr->ifgr_name)) break; if (ifg == NULL) { IFNET_RUNLOCK(); return (ENOENT); } if (ifgr->ifgr_len == 0) { TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) ifgr->ifgr_len += sizeof(ifgrq); IFNET_RUNLOCK(); return (0); } len = ifgr->ifgr_len; ifgp = ifgr->ifgr_groups; TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) { if (len < sizeof(ifgrq)) { IFNET_RUNLOCK(); return (EINVAL); } bzero(&ifgrq, sizeof ifgrq); strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname, sizeof(ifgrq.ifgrq_member)); if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) { IFNET_RUNLOCK(); return (error); } len -= sizeof(ifgrq); ifgp++; } IFNET_RUNLOCK(); return (0); } /* * Return counter values from counter(9)s stored in ifnet. */ uint64_t if_get_counter_default(struct ifnet *ifp, ift_counter cnt) { KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt)); return (counter_u64_fetch(ifp->if_counters[cnt])); } /* * Increase an ifnet counter. Usually used for counters shared * between the stack and a driver, but function supports them all. */ void if_inc_counter(struct ifnet *ifp, ift_counter cnt, int64_t inc) { KASSERT(cnt < IFCOUNTERS, ("%s: invalid cnt %d", __func__, cnt)); counter_u64_add(ifp->if_counters[cnt], inc); } /* * Copy data from ifnet to userland API structure if_data. */ void if_data_copy(struct ifnet *ifp, struct if_data *ifd) { ifd->ifi_type = ifp->if_type; ifd->ifi_physical = 0; ifd->ifi_addrlen = ifp->if_addrlen; ifd->ifi_hdrlen = ifp->if_hdrlen; ifd->ifi_link_state = ifp->if_link_state; ifd->ifi_vhid = 0; ifd->ifi_datalen = sizeof(struct if_data); ifd->ifi_mtu = ifp->if_mtu; ifd->ifi_metric = ifp->if_metric; ifd->ifi_baudrate = ifp->if_baudrate; ifd->ifi_hwassist = ifp->if_hwassist; ifd->ifi_epoch = ifp->if_epoch; ifd->ifi_lastchange = ifp->if_lastchange; ifd->ifi_ipackets = ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS); ifd->ifi_ierrors = ifp->if_get_counter(ifp, IFCOUNTER_IERRORS); ifd->ifi_opackets = ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS); ifd->ifi_oerrors = ifp->if_get_counter(ifp, IFCOUNTER_OERRORS); ifd->ifi_collisions = ifp->if_get_counter(ifp, IFCOUNTER_COLLISIONS); ifd->ifi_ibytes = ifp->if_get_counter(ifp, IFCOUNTER_IBYTES); ifd->ifi_obytes = ifp->if_get_counter(ifp, IFCOUNTER_OBYTES); ifd->ifi_imcasts = ifp->if_get_counter(ifp, IFCOUNTER_IMCASTS); ifd->ifi_omcasts = ifp->if_get_counter(ifp, IFCOUNTER_OMCASTS); ifd->ifi_iqdrops = ifp->if_get_counter(ifp, IFCOUNTER_IQDROPS); ifd->ifi_oqdrops = ifp->if_get_counter(ifp, IFCOUNTER_OQDROPS); ifd->ifi_noproto = ifp->if_get_counter(ifp, IFCOUNTER_NOPROTO); } /* * Wrapper functions for struct ifnet address list locking macros. These are * used by kernel modules to avoid encoding programming interface or binary * interface assumptions that may be violated when kernel-internal locking * approaches change. */ void if_addr_rlock(struct ifnet *ifp) { IF_ADDR_RLOCK(ifp); } void if_addr_runlock(struct ifnet *ifp) { IF_ADDR_RUNLOCK(ifp); } void if_maddr_rlock(if_t ifp) { IF_ADDR_RLOCK((struct ifnet *)ifp); } void if_maddr_runlock(if_t ifp) { IF_ADDR_RUNLOCK((struct ifnet *)ifp); } /* * Initialization, destruction and refcounting functions for ifaddrs. */ struct ifaddr * ifa_alloc(size_t size, int flags) { struct ifaddr *ifa; KASSERT(size >= sizeof(struct ifaddr), ("%s: invalid size %zu", __func__, size)); ifa = malloc(size, M_IFADDR, M_ZERO | flags); if (ifa == NULL) return (NULL); if ((ifa->ifa_opackets = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_ipackets = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_obytes = counter_u64_alloc(flags)) == NULL) goto fail; if ((ifa->ifa_ibytes = counter_u64_alloc(flags)) == NULL) goto fail; refcount_init(&ifa->ifa_refcnt, 1); return (ifa); fail: /* free(NULL) is okay */ counter_u64_free(ifa->ifa_opackets); counter_u64_free(ifa->ifa_ipackets); counter_u64_free(ifa->ifa_obytes); counter_u64_free(ifa->ifa_ibytes); free(ifa, M_IFADDR); return (NULL); } void ifa_ref(struct ifaddr *ifa) { refcount_acquire(&ifa->ifa_refcnt); } void ifa_free(struct ifaddr *ifa) { if (refcount_release(&ifa->ifa_refcnt)) { counter_u64_free(ifa->ifa_opackets); counter_u64_free(ifa->ifa_ipackets); counter_u64_free(ifa->ifa_obytes); counter_u64_free(ifa->ifa_ibytes); free(ifa, M_IFADDR); } } static int ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa, struct sockaddr *ia) { int error; struct rt_addrinfo info; struct sockaddr_dl null_sdl; struct ifnet *ifp; ifp = ifa->ifa_ifp; bzero(&info, sizeof(info)); if (cmd != RTM_DELETE) info.rti_ifp = V_loif; info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC; info.rti_info[RTAX_DST] = ia; info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl; link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type); error = rtrequest1_fib(cmd, &info, NULL, ifp->if_fib); if (error != 0) log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n", __func__, otype, if_name(ifp), error); return (error); } int ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) { return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia)); } int ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) { return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia)); } int ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) { return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia)); } /* * XXX: Because sockaddr_dl has deeper structure than the sockaddr * structs used to represent other address families, it is necessary * to perform a different comparison. */ #define sa_dl_equal(a1, a2) \ ((((const struct sockaddr_dl *)(a1))->sdl_len == \ ((const struct sockaddr_dl *)(a2))->sdl_len) && \ (bcmp(CLLADDR((const struct sockaddr_dl *)(a1)), \ CLLADDR((const struct sockaddr_dl *)(a2)), \ ((const struct sockaddr_dl *)(a1))->sdl_alen) == 0)) /* * Locate an interface based on a complete address. */ /*ARGSUSED*/ static struct ifaddr * ifa_ifwithaddr_internal(const struct sockaddr *addr, int getref) { struct ifnet *ifp; struct ifaddr *ifa; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (sa_equal(addr, ifa->ifa_addr)) { if (getref) ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto done; } /* IP6 doesn't have broadcast */ if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { if (getref) ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto done; } } IF_ADDR_RUNLOCK(ifp); } ifa = NULL; done: IFNET_RUNLOCK_NOSLEEP(); return (ifa); } struct ifaddr * ifa_ifwithaddr(const struct sockaddr *addr) { return (ifa_ifwithaddr_internal(addr, 1)); } int ifa_ifwithaddr_check(const struct sockaddr *addr) { return (ifa_ifwithaddr_internal(addr, 0) != NULL); } /* * Locate an interface based on the broadcast address. */ /* ARGSUSED */ struct ifaddr * ifa_ifwithbroadaddr(const struct sockaddr *addr, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && ifa->ifa_broadaddr->sa_len != 0 && sa_equal(ifa->ifa_broadaddr, addr)) { ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto done; } } IF_ADDR_RUNLOCK(ifp); } ifa = NULL; done: IFNET_RUNLOCK_NOSLEEP(); return (ifa); } /* * Locate the point to point interface with a given destination address. */ /*ARGSUSED*/ struct ifaddr * ifa_ifwithdstaddr(const struct sockaddr *addr, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((ifp->if_flags & IFF_POINTOPOINT) == 0) continue; if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto done; } } IF_ADDR_RUNLOCK(ifp); } ifa = NULL; done: IFNET_RUNLOCK_NOSLEEP(); return (ifa); } /* * Find an interface on a specific network. If many, choice * is most specific found. */ struct ifaddr * ifa_ifwithnet(const struct sockaddr *addr, int ignore_ptp, int fibnum) { struct ifnet *ifp; struct ifaddr *ifa; struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; /* * AF_LINK addresses can be looked up directly by their index number, * so do that if we can. */ if (af == AF_LINK) { const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)addr; if (sdl->sdl_index && sdl->sdl_index <= V_if_index) return (ifaddr_byindex(sdl->sdl_index)); } /* * Scan though each interface, looking for ones that have addresses * in this address family and the requested fib. Maintain a reference * on ifa_maybe once we find one, as we release the IF_ADDR_RLOCK() that * kept it stable when we move onto the next interface. */ IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if ((fibnum != RT_ALL_FIBS) && (ifp->if_fib != fibnum)) continue; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { const char *cp, *cp2, *cp3; if (ifa->ifa_addr->sa_family != af) next: continue; if (af == AF_INET && ifp->if_flags & IFF_POINTOPOINT && !ignore_ptp) { /* * This is a bit broken as it doesn't * take into account that the remote end may * be a single node in the network we are * looking for. * The trouble is that we don't know the * netmask for the remote end. */ if (ifa->ifa_dstaddr != NULL && sa_equal(addr, ifa->ifa_dstaddr)) { ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto done; } } else { /* * Scan all the bits in the ifa's address. * If a bit dissagrees with what we are * looking for, mask it with the netmask * to see if it really matters. * (A byte at a time) */ if (ifa->ifa_netmask == 0) continue; cp = addr_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; while (cp3 < cplim) if ((*cp++ ^ *cp2++) & *cp3++) goto next; /* next address! */ /* * If the netmask of what we just found * is more specific than what we had before * (if we had one), or if the virtual status * of new prefix is better than of the old one, * then remember the new one before continuing * to search for an even better one. */ if (ifa_maybe == NULL || ifa_preferred(ifa_maybe, ifa) || rn_refines((caddr_t)ifa->ifa_netmask, (caddr_t)ifa_maybe->ifa_netmask)) { if (ifa_maybe != NULL) ifa_free(ifa_maybe); ifa_maybe = ifa; ifa_ref(ifa_maybe); } } } IF_ADDR_RUNLOCK(ifp); } ifa = ifa_maybe; ifa_maybe = NULL; done: IFNET_RUNLOCK_NOSLEEP(); if (ifa_maybe != NULL) ifa_free(ifa_maybe); return (ifa); } /* * Find an interface address specific to an interface best matching * a given address. */ struct ifaddr * ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp) { struct ifaddr *ifa; const char *cp, *cp2, *cp3; char *cplim; struct ifaddr *ifa_maybe = NULL; u_int af = addr->sa_family; if (af >= AF_MAX) return (NULL); IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != af) continue; if (ifa_maybe == NULL) ifa_maybe = ifa; if (ifa->ifa_netmask == 0) { if (sa_equal(addr, ifa->ifa_addr) || (ifa->ifa_dstaddr && sa_equal(addr, ifa->ifa_dstaddr))) goto done; continue; } if (ifp->if_flags & IFF_POINTOPOINT) { if (sa_equal(addr, ifa->ifa_dstaddr)) goto done; } else { cp = addr->sa_data; cp2 = ifa->ifa_addr->sa_data; cp3 = ifa->ifa_netmask->sa_data; cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; for (; cp3 < cplim; cp3++) if ((*cp++ ^ *cp2++) & *cp3) break; if (cp3 == cplim) goto done; } } ifa = ifa_maybe; done: if (ifa != NULL) ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); return (ifa); } /* * See whether new ifa is better than current one: * 1) A non-virtual one is preferred over virtual. * 2) A virtual in master state preferred over any other state. * * Used in several address selecting functions. */ int ifa_preferred(struct ifaddr *cur, struct ifaddr *next) { return (cur->ifa_carp && (!next->ifa_carp || ((*carp_master_p)(next) && !(*carp_master_p)(cur)))); } #include /* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. * This should be moved to /sys/net/link.c eventually. */ static void link_rtrequest(int cmd, struct rtentry *rt, struct rt_addrinfo *info) { struct ifaddr *ifa, *oifa; struct sockaddr *dst; struct ifnet *ifp; if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) return; ifa = ifaof_ifpforaddr(dst, ifp); if (ifa) { oifa = rt->rt_ifa; rt->rt_ifa = ifa; ifa_free(oifa); if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, info); } } struct sockaddr_dl * link_alloc_sdl(size_t size, int flags) { return (malloc(size, M_TEMP, flags)); } void link_free_sdl(struct sockaddr *sa) { free(sa, M_TEMP); } /* * Fills in given sdl with interface basic info. * Returns pointer to filled sdl. */ struct sockaddr_dl * link_init_sdl(struct ifnet *ifp, struct sockaddr *paddr, u_char iftype) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)paddr; memset(sdl, 0, sizeof(struct sockaddr_dl)); sdl->sdl_len = sizeof(struct sockaddr_dl); sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = iftype; return (sdl); } /* * Mark an interface down and notify protocols of * the transition. */ static void if_unroute(struct ifnet *ifp, int flag, int fam) { struct ifaddr *ifa; KASSERT(flag == IFF_UP, ("if_unroute: flag != IFF_UP")); ifp->if_flags &= ~flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFDOWN, ifa->ifa_addr); ifp->if_qflush(ifp); if (ifp->if_carp) (*carp_linkstate_p)(ifp); rt_ifmsg(ifp); } /* * Mark an interface up and notify protocols of * the transition. */ static void if_route(struct ifnet *ifp, int flag, int fam) { struct ifaddr *ifa; KASSERT(flag == IFF_UP, ("if_route: flag != IFF_UP")); ifp->if_flags |= flag; getmicrotime(&ifp->if_lastchange); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family)) pfctlinput(PRC_IFUP, ifa->ifa_addr); if (ifp->if_carp) (*carp_linkstate_p)(ifp); rt_ifmsg(ifp); #ifdef INET6 in6_if_up(ifp); #endif } void (*vlan_link_state_p)(struct ifnet *); /* XXX: private from if_vlan */ void (*vlan_trunk_cap_p)(struct ifnet *); /* XXX: private from if_vlan */ struct ifnet *(*vlan_trunkdev_p)(struct ifnet *); struct ifnet *(*vlan_devat_p)(struct ifnet *, uint16_t); int (*vlan_tag_p)(struct ifnet *, uint16_t *); int (*vlan_setcookie_p)(struct ifnet *, void *); void *(*vlan_cookie_p)(struct ifnet *); /* * Handle a change in the interface link state. To avoid LORs * between driver lock and upper layer locks, as well as possible * recursions, we post event to taskqueue, and all job * is done in static do_link_state_change(). */ void if_link_state_change(struct ifnet *ifp, int link_state) { /* Return if state hasn't changed. */ if (ifp->if_link_state == link_state) return; ifp->if_link_state = link_state; taskqueue_enqueue(taskqueue_swi, &ifp->if_linktask); } static void do_link_state_change(void *arg, int pending) { struct ifnet *ifp = (struct ifnet *)arg; int link_state = ifp->if_link_state; CURVNET_SET(ifp->if_vnet); /* Notify that the link state has changed. */ rt_ifmsg(ifp); if (ifp->if_vlantrunk != NULL) (*vlan_link_state_p)(ifp); if ((ifp->if_type == IFT_ETHER || ifp->if_type == IFT_L2VLAN) && ifp->if_l2com != NULL) (*ng_ether_link_state_p)(ifp, link_state); if (ifp->if_carp) (*carp_linkstate_p)(ifp); if (ifp->if_bridge) (*bridge_linkstate_p)(ifp); if (ifp->if_lagg) (*lagg_linkstate_p)(ifp, link_state); if (IS_DEFAULT_VNET(curvnet)) devctl_notify("IFNET", ifp->if_xname, (link_state == LINK_STATE_UP) ? "LINK_UP" : "LINK_DOWN", NULL); if (pending > 1) if_printf(ifp, "%d link states coalesced\n", pending); if (log_link_state_change) log(LOG_NOTICE, "%s: link state changed to %s\n", ifp->if_xname, (link_state == LINK_STATE_UP) ? "UP" : "DOWN" ); EVENTHANDLER_INVOKE(ifnet_link_event, ifp, ifp->if_link_state); CURVNET_RESTORE(); } /* * Mark an interface down and notify protocols of * the transition. */ void if_down(struct ifnet *ifp) { if_unroute(ifp, IFF_UP, AF_UNSPEC); } /* * Mark an interface up and notify protocols of * the transition. */ void if_up(struct ifnet *ifp) { if_route(ifp, IFF_UP, AF_UNSPEC); } /* * Flush an interface queue. */ void if_qflush(struct ifnet *ifp) { struct mbuf *m, *n; struct ifaltq *ifq; ifq = &ifp->if_snd; IFQ_LOCK(ifq); #ifdef ALTQ if (ALTQ_IS_ENABLED(ifq)) ALTQ_PURGE(ifq); #endif n = ifq->ifq_head; while ((m = n) != 0) { n = m->m_nextpkt; m_freem(m); } ifq->ifq_head = 0; ifq->ifq_tail = 0; ifq->ifq_len = 0; IFQ_UNLOCK(ifq); } /* * Map interface name to interface structure pointer, with or without * returning a reference. */ struct ifnet * ifunit_ref(const char *name) { struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (strncmp(name, ifp->if_xname, IFNAMSIZ) == 0 && !(ifp->if_flags & IFF_DYING)) break; } if (ifp != NULL) if_ref(ifp); IFNET_RUNLOCK_NOSLEEP(); return (ifp); } struct ifnet * ifunit(const char *name) { struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (strncmp(name, ifp->if_xname, IFNAMSIZ) == 0) break; } IFNET_RUNLOCK_NOSLEEP(); return (ifp); } /* * Hardware specific interface ioctls. */ static int ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) { struct ifreq *ifr; int error = 0; int new_flags, temp_flags; size_t namelen, onamelen; size_t descrlen; char *descrbuf, *odescrbuf; char new_name[IFNAMSIZ]; struct ifaddr *ifa; struct sockaddr_dl *sdl; ifr = (struct ifreq *)data; switch (cmd) { case SIOCGIFINDEX: ifr->ifr_index = ifp->if_index; break; case SIOCGIFFLAGS: temp_flags = ifp->if_flags | ifp->if_drv_flags; ifr->ifr_flags = temp_flags & 0xffff; ifr->ifr_flagshigh = temp_flags >> 16; break; case SIOCGIFCAP: ifr->ifr_reqcap = ifp->if_capabilities; ifr->ifr_curcap = ifp->if_capenable; break; #ifdef MAC case SIOCGIFMAC: error = mac_ifnet_ioctl_get(td->td_ucred, ifr, ifp); break; #endif case SIOCGIFMETRIC: ifr->ifr_metric = ifp->if_metric; break; case SIOCGIFMTU: ifr->ifr_mtu = ifp->if_mtu; break; case SIOCGIFPHYS: /* XXXGL: did this ever worked? */ ifr->ifr_phys = 0; break; case SIOCGIFDESCR: error = 0; sx_slock(&ifdescr_sx); if (ifp->if_description == NULL) error = ENOMSG; else { /* space for terminating nul */ descrlen = strlen(ifp->if_description) + 1; if (ifr->ifr_buffer.length < descrlen) ifr->ifr_buffer.buffer = NULL; else error = copyout(ifp->if_description, ifr->ifr_buffer.buffer, descrlen); ifr->ifr_buffer.length = descrlen; } sx_sunlock(&ifdescr_sx); break; case SIOCSIFDESCR: error = priv_check(td, PRIV_NET_SETIFDESCR); if (error) return (error); /* * Copy only (length-1) bytes to make sure that * if_description is always nul terminated. The * length parameter is supposed to count the * terminating nul in. */ if (ifr->ifr_buffer.length > ifdescr_maxlen) return (ENAMETOOLONG); else if (ifr->ifr_buffer.length == 0) descrbuf = NULL; else { descrbuf = malloc(ifr->ifr_buffer.length, M_IFDESCR, M_WAITOK | M_ZERO); error = copyin(ifr->ifr_buffer.buffer, descrbuf, ifr->ifr_buffer.length - 1); if (error) { free(descrbuf, M_IFDESCR); break; } } sx_xlock(&ifdescr_sx); odescrbuf = ifp->if_description; ifp->if_description = descrbuf; sx_xunlock(&ifdescr_sx); getmicrotime(&ifp->if_lastchange); free(odescrbuf, M_IFDESCR); break; case SIOCGIFFIB: ifr->ifr_fib = ifp->if_fib; break; case SIOCSIFFIB: error = priv_check(td, PRIV_NET_SETIFFIB); if (error) return (error); if (ifr->ifr_fib >= rt_numfibs) return (EINVAL); ifp->if_fib = ifr->ifr_fib; break; case SIOCSIFFLAGS: error = priv_check(td, PRIV_NET_SETIFFLAGS); if (error) return (error); /* * Currently, no driver owned flags pass the IFF_CANTCHANGE * check, so we don't need special handling here yet. */ new_flags = (ifr->ifr_flags & 0xffff) | (ifr->ifr_flagshigh << 16); if (ifp->if_flags & IFF_UP && (new_flags & IFF_UP) == 0) { if_down(ifp); } else if (new_flags & IFF_UP && (ifp->if_flags & IFF_UP) == 0) { if_up(ifp); } /* See if permanently promiscuous mode bit is about to flip */ if ((ifp->if_flags ^ new_flags) & IFF_PPROMISC) { if (new_flags & IFF_PPROMISC) ifp->if_flags |= IFF_PROMISC; else if (ifp->if_pcount == 0) ifp->if_flags &= ~IFF_PROMISC; log(LOG_INFO, "%s: permanently promiscuous mode %s\n", ifp->if_xname, (new_flags & IFF_PPROMISC) ? "enabled" : "disabled"); } ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | (new_flags &~ IFF_CANTCHANGE); if (ifp->if_ioctl) { (void) (*ifp->if_ioctl)(ifp, cmd, data); } getmicrotime(&ifp->if_lastchange); break; case SIOCSIFCAP: error = priv_check(td, PRIV_NET_SETIFCAP); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); if (ifr->ifr_reqcap & ~ifp->if_capabilities) return (EINVAL); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; #ifdef MAC case SIOCSIFMAC: error = mac_ifnet_ioctl_set(td->td_ucred, ifr, ifp); break; #endif case SIOCSIFNAME: error = priv_check(td, PRIV_NET_SETIFNAME); if (error) return (error); error = copyinstr(ifr->ifr_data, new_name, IFNAMSIZ, NULL); if (error != 0) return (error); if (new_name[0] == '\0') return (EINVAL); if (ifunit(new_name) != NULL) return (EEXIST); /* * XXX: Locking. Nothing else seems to lock if_flags, * and there are numerous other races with the * ifunit() checks not being atomic with namespace * changes (renames, vmoves, if_attach, etc). */ ifp->if_flags |= IFF_RENAMING; /* Announce the departure of the interface. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); EVENTHANDLER_INVOKE(ifnet_departure_event, ifp); log(LOG_INFO, "%s: changing name to '%s'\n", ifp->if_xname, new_name); IF_ADDR_WLOCK(ifp); strlcpy(ifp->if_xname, new_name, sizeof(ifp->if_xname)); ifa = ifp->if_addr; sdl = (struct sockaddr_dl *)ifa->ifa_addr; namelen = strlen(new_name); onamelen = sdl->sdl_nlen; /* * Move the address if needed. This is safe because we * allocate space for a name of length IFNAMSIZ when we * create this in if_attach(). */ if (namelen != onamelen) { bcopy(sdl->sdl_data + onamelen, sdl->sdl_data + namelen, sdl->sdl_alen); } bcopy(new_name, sdl->sdl_data, namelen); sdl->sdl_nlen = namelen; sdl = (struct sockaddr_dl *)ifa->ifa_netmask; bzero(sdl->sdl_data, onamelen); while (namelen != 0) sdl->sdl_data[--namelen] = 0xff; IF_ADDR_WUNLOCK(ifp); EVENTHANDLER_INVOKE(ifnet_arrival_event, ifp); /* Announce the return of the interface. */ rt_ifannouncemsg(ifp, IFAN_ARRIVAL); ifp->if_flags &= ~IFF_RENAMING; break; #ifdef VIMAGE case SIOCSIFVNET: error = priv_check(td, PRIV_NET_SETIFVNET); if (error) return (error); error = if_vmove_loan(td, ifp, ifr->ifr_name, ifr->ifr_jid); break; #endif case SIOCSIFMETRIC: error = priv_check(td, PRIV_NET_SETIFMETRIC); if (error) return (error); ifp->if_metric = ifr->ifr_metric; getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYS: error = priv_check(td, PRIV_NET_SETIFPHYS); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCSIFMTU: { u_long oldmtu = ifp->if_mtu; error = priv_check(td, PRIV_NET_SETIFMTU); if (error) return (error); if (ifr->ifr_mtu < IF_MINMTU || ifr->ifr_mtu > IF_MAXMTU) return (EINVAL); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) { getmicrotime(&ifp->if_lastchange); rt_ifmsg(ifp); } /* * If the link MTU changed, do network layer specific procedure. */ if (ifp->if_mtu != oldmtu) { #ifdef INET6 nd6_setmtu(ifp); #endif rt_updatemtu(ifp); } break; } case SIOCADDMULTI: case SIOCDELMULTI: if (cmd == SIOCADDMULTI) error = priv_check(td, PRIV_NET_ADDMULTI); else error = priv_check(td, PRIV_NET_DELMULTI); if (error) return (error); /* Don't allow group membership on non-multicast interfaces. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return (EOPNOTSUPP); /* Don't let users screw up protocols' entries. */ if (ifr->ifr_addr.sa_family != AF_LINK) return (EINVAL); if (cmd == SIOCADDMULTI) { struct ifmultiaddr *ifma; /* * Userland is only permitted to join groups once * via the if_addmulti() KPI, because it cannot hold * struct ifmultiaddr * between calls. It may also * lose a race while we check if the membership * already exists. */ IF_ADDR_RLOCK(ifp); ifma = if_findmulti(ifp, &ifr->ifr_addr); IF_ADDR_RUNLOCK(ifp); if (ifma != NULL) error = EADDRINUSE; else error = if_addmulti(ifp, &ifr->ifr_addr, &ifma); } else { error = if_delmulti(ifp, &ifr->ifr_addr); } if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCSIFPHYADDR: case SIOCDIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif case SIOCSIFMEDIA: case SIOCSIFGENERIC: error = priv_check(td, PRIV_NET_HWIOCTL); if (error) return (error); if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); if (error == 0) getmicrotime(&ifp->if_lastchange); break; case SIOCGIFSTATUS: case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: case SIOCGIFMEDIA: case SIOCGIFXMEDIA: case SIOCGIFGENERIC: if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); error = (*ifp->if_ioctl)(ifp, cmd, data); break; case SIOCSIFLLADDR: error = priv_check(td, PRIV_NET_SETLLADDR); if (error) return (error); error = if_setlladdr(ifp, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len); break; case SIOCAIFGROUP: { struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr; error = priv_check(td, PRIV_NET_ADDIFGROUP); if (error) return (error); if ((error = if_addgroup(ifp, ifgr->ifgr_group))) return (error); break; } case SIOCGIFGROUP: if ((error = if_getgroup((struct ifgroupreq *)ifr, ifp))) return (error); break; case SIOCDIFGROUP: { struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr; error = priv_check(td, PRIV_NET_DELIFGROUP); if (error) return (error); if ((error = if_delgroup(ifp, ifgr->ifgr_group))) return (error); break; } default: error = ENOIOCTL; break; } return (error); } #ifdef COMPAT_FREEBSD32 struct ifconf32 { int32_t ifc_len; union { uint32_t ifcu_buf; uint32_t ifcu_req; } ifc_ifcu; }; #define SIOCGIFCONF32 _IOWR('i', 36, struct ifconf32) #endif /* * Interface ioctls. */ int ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) { struct ifnet *ifp; struct ifreq *ifr; int error; int oif_flags; CURVNET_SET(so->so_vnet); switch (cmd) { case SIOCGIFCONF: error = ifconf(cmd, data); CURVNET_RESTORE(); return (error); #ifdef COMPAT_FREEBSD32 case SIOCGIFCONF32: { struct ifconf32 *ifc32; struct ifconf ifc; ifc32 = (struct ifconf32 *)data; ifc.ifc_len = ifc32->ifc_len; ifc.ifc_buf = PTRIN(ifc32->ifc_buf); error = ifconf(SIOCGIFCONF, (void *)&ifc); CURVNET_RESTORE(); if (error == 0) ifc32->ifc_len = ifc.ifc_len; return (error); } #endif } ifr = (struct ifreq *)data; switch (cmd) { #ifdef VIMAGE case SIOCSIFRVNET: error = priv_check(td, PRIV_NET_SETIFVNET); if (error == 0) error = if_vmove_reclaim(td, ifr->ifr_name, ifr->ifr_jid); CURVNET_RESTORE(); return (error); #endif case SIOCIFCREATE: case SIOCIFCREATE2: error = priv_check(td, PRIV_NET_IFCREATE); if (error == 0) error = if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name), cmd == SIOCIFCREATE2 ? ifr->ifr_data : NULL); CURVNET_RESTORE(); return (error); case SIOCIFDESTROY: error = priv_check(td, PRIV_NET_IFDESTROY); if (error == 0) error = if_clone_destroy(ifr->ifr_name); CURVNET_RESTORE(); return (error); case SIOCIFGCLONERS: error = if_clone_list((struct if_clonereq *)data); CURVNET_RESTORE(); return (error); case SIOCGIFGMEMB: error = if_getgroupmembers((struct ifgroupreq *)data); CURVNET_RESTORE(); return (error); #if defined(INET) || defined(INET6) case SIOCSVH: case SIOCGVH: if (carp_ioctl_p == NULL) error = EPROTONOSUPPORT; else error = (*carp_ioctl_p)(ifr, cmd, td); CURVNET_RESTORE(); return (error); #endif } ifp = ifunit_ref(ifr->ifr_name); if (ifp == NULL) { CURVNET_RESTORE(); return (ENXIO); } error = ifhwioctl(cmd, ifp, data, td); if (error != ENOIOCTL) { if_rele(ifp); CURVNET_RESTORE(); return (error); } oif_flags = ifp->if_flags; if (so->so_proto == NULL) { if_rele(ifp); CURVNET_RESTORE(); return (EOPNOTSUPP); } /* * Pass the request on to the socket control method, and if the * latter returns EOPNOTSUPP, directly to the interface. * * Make an exception for the legacy SIOCSIF* requests. Drivers * trust SIOCSIFADDR et al to come from an already privileged * layer, and do not perform any credentials checks or input * validation. */ error = ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, data, ifp, td)); if (error == EOPNOTSUPP && ifp != NULL && ifp->if_ioctl != NULL && cmd != SIOCSIFADDR && cmd != SIOCSIFBRDADDR && cmd != SIOCSIFDSTADDR && cmd != SIOCSIFNETMASK) error = (*ifp->if_ioctl)(ifp, cmd, data); if ((oif_flags ^ ifp->if_flags) & IFF_UP) { #ifdef INET6 if (ifp->if_flags & IFF_UP) in6_if_up(ifp); #endif } if_rele(ifp); CURVNET_RESTORE(); return (error); } /* * The code common to handling reference counted flags, * e.g., in ifpromisc() and if_allmulti(). * The "pflag" argument can specify a permanent mode flag to check, * such as IFF_PPROMISC for promiscuous mode; should be 0 if none. * * Only to be used on stack-owned flags, not driver-owned flags. */ static int if_setflag(struct ifnet *ifp, int flag, int pflag, int *refcount, int onswitch) { struct ifreq ifr; int error; int oldflags, oldcount; /* Sanity checks to catch programming errors */ KASSERT((flag & (IFF_DRV_OACTIVE|IFF_DRV_RUNNING)) == 0, ("%s: setting driver-owned flag %d", __func__, flag)); if (onswitch) KASSERT(*refcount >= 0, ("%s: increment negative refcount %d for flag %d", __func__, *refcount, flag)); else KASSERT(*refcount > 0, ("%s: decrement non-positive refcount %d for flag %d", __func__, *refcount, flag)); /* In case this mode is permanent, just touch refcount */ if (ifp->if_flags & pflag) { *refcount += onswitch ? 1 : -1; return (0); } /* Save ifnet parameters for if_ioctl() may fail */ oldcount = *refcount; oldflags = ifp->if_flags; /* * See if we aren't the only and touching refcount is enough. * Actually toggle interface flag if we are the first or last. */ if (onswitch) { if ((*refcount)++) return (0); ifp->if_flags |= flag; } else { if (--(*refcount)) return (0); ifp->if_flags &= ~flag; } /* Call down the driver since we've changed interface flags */ if (ifp->if_ioctl == NULL) { error = EOPNOTSUPP; goto recover; } ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; error = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); if (error) goto recover; /* Notify userland that interface flags have changed */ rt_ifmsg(ifp); return (0); recover: /* Recover after driver error */ *refcount = oldcount; ifp->if_flags = oldflags; return (error); } /* * Set/clear promiscuous mode on interface ifp based on the truth value * of pswitch. The calls are reference counted so that only the first * "on" request actually has an effect, as does the final "off" request. * Results are undefined if the "off" and "on" requests are not matched. */ int ifpromisc(struct ifnet *ifp, int pswitch) { int error; int oldflags = ifp->if_flags; error = if_setflag(ifp, IFF_PROMISC, IFF_PPROMISC, &ifp->if_pcount, pswitch); /* If promiscuous mode status has changed, log a message */ if (error == 0 && ((ifp->if_flags ^ oldflags) & IFF_PROMISC)) log(LOG_INFO, "%s: promiscuous mode %s\n", ifp->if_xname, (ifp->if_flags & IFF_PROMISC) ? "enabled" : "disabled"); return (error); } /* * Return interface configuration * of system. List may be used * in later ioctl's (above) to get * other information. */ /*ARGSUSED*/ static int ifconf(u_long cmd, caddr_t data) { struct ifconf *ifc = (struct ifconf *)data; struct ifnet *ifp; struct ifaddr *ifa; struct ifreq ifr; struct sbuf *sb; int error, full = 0, valid_len, max_len; /* Limit initial buffer size to MAXPHYS to avoid DoS from userspace. */ max_len = MAXPHYS - 1; /* Prevent hostile input from being able to crash the system */ if (ifc->ifc_len <= 0) return (EINVAL); again: if (ifc->ifc_len <= max_len) { max_len = ifc->ifc_len; full = 1; } sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN); max_len = 0; valid_len = 0; IFNET_RLOCK(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { int addrs; /* * Zero the ifr_name buffer to make sure we don't * disclose the contents of the stack. */ memset(ifr.ifr_name, 0, sizeof(ifr.ifr_name)); if (strlcpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)) >= sizeof(ifr.ifr_name)) { sbuf_delete(sb); IFNET_RUNLOCK(); return (ENAMETOOLONG); } addrs = 0; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct sockaddr *sa = ifa->ifa_addr; if (prison_if(curthread->td_ucred, sa) != 0) continue; addrs++; if (sa->sa_len <= sizeof(*sa)) { ifr.ifr_addr = *sa; sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); } else { sbuf_bcat(sb, &ifr, offsetof(struct ifreq, ifr_addr)); max_len += offsetof(struct ifreq, ifr_addr); sbuf_bcat(sb, sa, sa->sa_len); max_len += sa->sa_len; } if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } IF_ADDR_RUNLOCK(ifp); if (addrs == 0) { bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } } IFNET_RUNLOCK(); /* * If we didn't allocate enough space (uncommon), try again. If * we have already allocated as much space as we are allowed, * return what we've got. */ if (valid_len != max_len && !full) { sbuf_delete(sb); goto again; } ifc->ifc_len = valid_len; sbuf_finish(sb); error = copyout(sbuf_data(sb), ifc->ifc_req, ifc->ifc_len); sbuf_delete(sb); return (error); } /* * Just like ifpromisc(), but for all-multicast-reception mode. */ int if_allmulti(struct ifnet *ifp, int onswitch) { return (if_setflag(ifp, IFF_ALLMULTI, 0, &ifp->if_amcount, onswitch)); } struct ifmultiaddr * if_findmulti(struct ifnet *ifp, const struct sockaddr *sa) { struct ifmultiaddr *ifma; IF_ADDR_LOCK_ASSERT(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (sa->sa_family == AF_LINK) { if (sa_dl_equal(ifma->ifma_addr, sa)) break; } else { if (sa_equal(ifma->ifma_addr, sa)) break; } } return ifma; } /* * Allocate a new ifmultiaddr and initialize based on passed arguments. We * make copies of passed sockaddrs. The ifmultiaddr will not be added to * the ifnet multicast address list here, so the caller must do that and * other setup work (such as notifying the device driver). The reference * count is initialized to 1. */ static struct ifmultiaddr * if_allocmulti(struct ifnet *ifp, struct sockaddr *sa, struct sockaddr *llsa, int mflags) { struct ifmultiaddr *ifma; struct sockaddr *dupsa; ifma = malloc(sizeof *ifma, M_IFMADDR, mflags | M_ZERO); if (ifma == NULL) return (NULL); dupsa = malloc(sa->sa_len, M_IFMADDR, mflags); if (dupsa == NULL) { free(ifma, M_IFMADDR); return (NULL); } bcopy(sa, dupsa, sa->sa_len); ifma->ifma_addr = dupsa; ifma->ifma_ifp = ifp; ifma->ifma_refcount = 1; ifma->ifma_protospec = NULL; if (llsa == NULL) { ifma->ifma_lladdr = NULL; return (ifma); } dupsa = malloc(llsa->sa_len, M_IFMADDR, mflags); if (dupsa == NULL) { free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); return (NULL); } bcopy(llsa, dupsa, llsa->sa_len); ifma->ifma_lladdr = dupsa; return (ifma); } /* * if_freemulti: free ifmultiaddr structure and possibly attached related * addresses. The caller is responsible for implementing reference * counting, notifying the driver, handling routing messages, and releasing * any dependent link layer state. */ static void if_freemulti(struct ifmultiaddr *ifma) { KASSERT(ifma->ifma_refcount == 0, ("if_freemulti: refcount %d", ifma->ifma_refcount)); if (ifma->ifma_lladdr != NULL) free(ifma->ifma_lladdr, M_IFMADDR); free(ifma->ifma_addr, M_IFMADDR); free(ifma, M_IFMADDR); } /* * Register an additional multicast address with a network interface. * * - If the address is already present, bump the reference count on the * address and return. * - If the address is not link-layer, look up a link layer address. * - Allocate address structures for one or both addresses, and attach to the * multicast address list on the interface. If automatically adding a link * layer address, the protocol address will own a reference to the link * layer address, to be freed when it is freed. * - Notify the network device driver of an addition to the multicast address * list. * * 'sa' points to caller-owned memory with the desired multicast address. * * 'retifma' will be used to return a pointer to the resulting multicast * address reference, if desired. */ int if_addmulti(struct ifnet *ifp, struct sockaddr *sa, struct ifmultiaddr **retifma) { struct ifmultiaddr *ifma, *ll_ifma; struct sockaddr *llsa; struct sockaddr_dl sdl; int error; /* * If the address is already present, return a new reference to it; * otherwise, allocate storage and set up a new address. */ IF_ADDR_WLOCK(ifp); ifma = if_findmulti(ifp, sa); if (ifma != NULL) { ifma->ifma_refcount++; if (retifma != NULL) *retifma = ifma; IF_ADDR_WUNLOCK(ifp); return (0); } /* * The address isn't already present; resolve the protocol address * into a link layer address, and then look that up, bump its * refcount or allocate an ifma for that also. * Most link layer resolving functions returns address data which * fits inside default sockaddr_dl structure. However callback * can allocate another sockaddr structure, in that case we need to * free it later. */ llsa = NULL; ll_ifma = NULL; if (ifp->if_resolvemulti != NULL) { /* Provide called function with buffer size information */ sdl.sdl_len = sizeof(sdl); llsa = (struct sockaddr *)&sdl; error = ifp->if_resolvemulti(ifp, &llsa, sa); if (error) goto unlock_out; } /* * Allocate the new address. Don't hook it up yet, as we may also * need to allocate a link layer multicast address. */ ifma = if_allocmulti(ifp, sa, llsa, M_NOWAIT); if (ifma == NULL) { error = ENOMEM; goto free_llsa_out; } /* * If a link layer address is found, we'll need to see if it's * already present in the address list, or allocate is as well. * When this block finishes, the link layer address will be on the * list. */ if (llsa != NULL) { ll_ifma = if_findmulti(ifp, llsa); if (ll_ifma == NULL) { ll_ifma = if_allocmulti(ifp, llsa, NULL, M_NOWAIT); if (ll_ifma == NULL) { --ifma->ifma_refcount; if_freemulti(ifma); error = ENOMEM; goto free_llsa_out; } TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ll_ifma, ifma_link); } else ll_ifma->ifma_refcount++; ifma->ifma_llifma = ll_ifma; } /* * We now have a new multicast address, ifma, and possibly a new or * referenced link layer address. Add the primary address to the * ifnet address list. */ TAILQ_INSERT_HEAD(&ifp->if_multiaddrs, ifma, ifma_link); if (retifma != NULL) *retifma = ifma; /* * Must generate the message while holding the lock so that 'ifma' * pointer is still valid. */ rt_newmaddrmsg(RTM_NEWMADDR, ifma); IF_ADDR_WUNLOCK(ifp); /* * We are certain we have added something, so call down to the * interface to let them know about it. */ if (ifp->if_ioctl != NULL) { (void) (*ifp->if_ioctl)(ifp, SIOCADDMULTI, 0); } if ((llsa != NULL) && (llsa != (struct sockaddr *)&sdl)) link_free_sdl(llsa); return (0); free_llsa_out: if ((llsa != NULL) && (llsa != (struct sockaddr *)&sdl)) link_free_sdl(llsa); unlock_out: IF_ADDR_WUNLOCK(ifp); return (error); } /* * Delete a multicast group membership by network-layer group address. * * Returns ENOENT if the entry could not be found. If ifp no longer * exists, results are undefined. This entry point should only be used * from subsystems which do appropriate locking to hold ifp for the * duration of the call. * Network-layer protocol domains must use if_delmulti_ifma(). */ int if_delmulti(struct ifnet *ifp, struct sockaddr *sa) { struct ifmultiaddr *ifma; int lastref; #ifdef INVARIANTS struct ifnet *oifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(oifp, &V_ifnet, if_link) if (ifp == oifp) break; if (ifp != oifp) ifp = NULL; IFNET_RUNLOCK_NOSLEEP(); KASSERT(ifp != NULL, ("%s: ifnet went away", __func__)); #endif if (ifp == NULL) return (ENOENT); IF_ADDR_WLOCK(ifp); lastref = 0; ifma = if_findmulti(ifp, sa); if (ifma != NULL) lastref = if_delmulti_locked(ifp, ifma, 0); IF_ADDR_WUNLOCK(ifp); if (ifma == NULL) return (ENOENT); if (lastref && ifp->if_ioctl != NULL) { (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, 0); } return (0); } /* * Delete all multicast group membership for an interface. * Should be used to quickly flush all multicast filters. */ void if_delallmulti(struct ifnet *ifp) { struct ifmultiaddr *ifma; struct ifmultiaddr *next; IF_ADDR_WLOCK(ifp); TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) if_delmulti_locked(ifp, ifma, 0); IF_ADDR_WUNLOCK(ifp); } /* * Delete a multicast group membership by group membership pointer. * Network-layer protocol domains must use this routine. * * It is safe to call this routine if the ifp disappeared. */ void if_delmulti_ifma(struct ifmultiaddr *ifma) { struct ifnet *ifp; int lastref; ifp = ifma->ifma_ifp; #ifdef DIAGNOSTIC if (ifp == NULL) { printf("%s: ifma_ifp seems to be detached\n", __func__); } else { struct ifnet *oifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(oifp, &V_ifnet, if_link) if (ifp == oifp) break; if (ifp != oifp) { printf("%s: ifnet %p disappeared\n", __func__, ifp); ifp = NULL; } IFNET_RUNLOCK_NOSLEEP(); } #endif /* * If and only if the ifnet instance exists: Acquire the address lock. */ if (ifp != NULL) IF_ADDR_WLOCK(ifp); lastref = if_delmulti_locked(ifp, ifma, 0); if (ifp != NULL) { /* * If and only if the ifnet instance exists: * Release the address lock. * If the group was left: update the hardware hash filter. */ IF_ADDR_WUNLOCK(ifp); if (lastref && ifp->if_ioctl != NULL) { (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, 0); } } } /* * Perform deletion of network-layer and/or link-layer multicast address. * * Return 0 if the reference count was decremented. * Return 1 if the final reference was released, indicating that the * hardware hash filter should be reprogrammed. */ static int if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching) { struct ifmultiaddr *ll_ifma; if (ifp != NULL && ifma->ifma_ifp != NULL) { KASSERT(ifma->ifma_ifp == ifp, ("%s: inconsistent ifp %p", __func__, ifp)); IF_ADDR_WLOCK_ASSERT(ifp); } ifp = ifma->ifma_ifp; /* * If the ifnet is detaching, null out references to ifnet, * so that upper protocol layers will notice, and not attempt * to obtain locks for an ifnet which no longer exists. The * routing socket announcement must happen before the ifnet * instance is detached from the system. */ if (detaching) { #ifdef DIAGNOSTIC printf("%s: detaching ifnet instance %p\n", __func__, ifp); #endif /* * ifp may already be nulled out if we are being reentered * to delete the ll_ifma. */ if (ifp != NULL) { rt_newmaddrmsg(RTM_DELMADDR, ifma); ifma->ifma_ifp = NULL; } } if (--ifma->ifma_refcount > 0) return 0; /* * If this ifma is a network-layer ifma, a link-layer ifma may * have been associated with it. Release it first if so. */ ll_ifma = ifma->ifma_llifma; if (ll_ifma != NULL) { KASSERT(ifma->ifma_lladdr != NULL, ("%s: llifma w/o lladdr", __func__)); if (detaching) ll_ifma->ifma_ifp = NULL; /* XXX */ if (--ll_ifma->ifma_refcount == 0) { if (ifp != NULL) { TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifma_link); } if_freemulti(ll_ifma); } } if (ifp != NULL) TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link); if_freemulti(ifma); /* * The last reference to this instance of struct ifmultiaddr * was released; the hardware should be notified of this change. */ return 1; } /* * Set the link layer address on an interface. * * At this time we only support certain types of interfaces, * and we don't allow the length of the address to change. * * Set noinline to be dtrace-friendly */ __noinline int if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len) { struct sockaddr_dl *sdl; struct ifaddr *ifa; struct ifreq ifr; IF_ADDR_RLOCK(ifp); ifa = ifp->if_addr; if (ifa == NULL) { IF_ADDR_RUNLOCK(ifp); return (EINVAL); } ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); sdl = (struct sockaddr_dl *)ifa->ifa_addr; if (sdl == NULL) { ifa_free(ifa); return (EINVAL); } if (len != sdl->sdl_alen) { /* don't allow length to change */ ifa_free(ifa); return (EINVAL); } switch (ifp->if_type) { case IFT_ETHER: case IFT_FDDI: case IFT_XETHER: case IFT_ISO88025: case IFT_L2VLAN: case IFT_BRIDGE: case IFT_ARCNET: case IFT_IEEE8023ADLAG: case IFT_IEEE80211: bcopy(lladdr, LLADDR(sdl), len); ifa_free(ifa); break; default: ifa_free(ifa); return (ENODEV); } /* * If the interface is already up, we need * to re-init it in order to reprogram its * address filter. */ if ((ifp->if_flags & IFF_UP) != 0) { if (ifp->if_ioctl) { ifp->if_flags &= ~IFF_UP; ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); ifp->if_flags |= IFF_UP; ifr.ifr_flags = ifp->if_flags & 0xffff; ifr.ifr_flagshigh = ifp->if_flags >> 16; (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); } } EVENTHANDLER_INVOKE(iflladdr_event, ifp); + return (0); +} + +/* + * Compat function for handling basic encapsulation requests. + * Not converted stacks (FDDI, IB, ..) supports traditional + * output model: ARP (and other similar L2 protocols) are handled + * inside output routine, arpresolve/nd6_resolve() returns MAC + * address instead of full prepend. + * + * This function creates calculated header==MAC for IPv4/IPv6 and + * returns EAFNOSUPPORT (which is then handled in ARP code) for other + * address families. + */ +static int +if_requestencap_default(struct ifnet *ifp, struct if_encap_req *req) +{ + + if (req->rtype != IFENCAP_LL) + return (EOPNOTSUPP); + + if (req->bufsize < req->lladdr_len) + return (ENOMEM); + + switch (req->family) { + case AF_INET: + case AF_INET6: + break; + default: + return (EAFNOSUPPORT); + } + + /* Copy lladdr to storage as is */ + memmove(req->buf, req->lladdr, req->lladdr_len); + req->bufsize = req->lladdr_len; + req->lladdr_off = 0; + return (0); } /* * The name argument must be a pointer to storage which will last as * long as the interface does. For physical devices, the result of * device_get_name(dev) is a good choice and for pseudo-devices a * static string works well. */ void if_initname(struct ifnet *ifp, const char *name, int unit) { ifp->if_dname = name; ifp->if_dunit = unit; if (unit != IF_DUNIT_NONE) snprintf(ifp->if_xname, IFNAMSIZ, "%s%d", name, unit); else strlcpy(ifp->if_xname, name, IFNAMSIZ); } int if_printf(struct ifnet *ifp, const char * fmt, ...) { va_list ap; int retval; retval = printf("%s: ", ifp->if_xname); va_start(ap, fmt); retval += vprintf(fmt, ap); va_end(ap); return (retval); } void if_start(struct ifnet *ifp) { (*(ifp)->if_start)(ifp); } /* * Backwards compatibility interface for drivers * that have not implemented it */ static int if_transmit(struct ifnet *ifp, struct mbuf *m) { int error; IFQ_HANDOFF(ifp, m, error); return (error); } static void if_input_default(struct ifnet *ifp __unused, struct mbuf *m) { m_freem(m); } int if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust) { int active = 0; IF_LOCK(ifq); if (_IF_QFULL(ifq)) { IF_UNLOCK(ifq); if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1); m_freem(m); return (0); } if (ifp != NULL) { if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len + adjust); if (m->m_flags & (M_BCAST|M_MCAST)) if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); active = ifp->if_drv_flags & IFF_DRV_OACTIVE; } _IF_ENQUEUE(ifq, m); IF_UNLOCK(ifq); if (ifp != NULL && !active) (*(ifp)->if_start)(ifp); return (1); } void if_register_com_alloc(u_char type, if_com_alloc_t *a, if_com_free_t *f) { KASSERT(if_com_alloc[type] == NULL, ("if_register_com_alloc: %d already registered", type)); KASSERT(if_com_free[type] == NULL, ("if_register_com_alloc: %d free already registered", type)); if_com_alloc[type] = a; if_com_free[type] = f; } void if_deregister_com_alloc(u_char type) { KASSERT(if_com_alloc[type] != NULL, ("if_deregister_com_alloc: %d not registered", type)); KASSERT(if_com_free[type] != NULL, ("if_deregister_com_alloc: %d free not registered", type)); if_com_alloc[type] = NULL; if_com_free[type] = NULL; } /* API for driver access to network stack owned ifnet.*/ uint64_t if_setbaudrate(struct ifnet *ifp, uint64_t baudrate) { uint64_t oldbrate; oldbrate = ifp->if_baudrate; ifp->if_baudrate = baudrate; return (oldbrate); } uint64_t if_getbaudrate(if_t ifp) { return (((struct ifnet *)ifp)->if_baudrate); } int if_setcapabilities(if_t ifp, int capabilities) { ((struct ifnet *)ifp)->if_capabilities = capabilities; return (0); } int if_setcapabilitiesbit(if_t ifp, int setbit, int clearbit) { ((struct ifnet *)ifp)->if_capabilities |= setbit; ((struct ifnet *)ifp)->if_capabilities &= ~clearbit; return (0); } int if_getcapabilities(if_t ifp) { return ((struct ifnet *)ifp)->if_capabilities; } int if_setcapenable(if_t ifp, int capabilities) { ((struct ifnet *)ifp)->if_capenable = capabilities; return (0); } int if_setcapenablebit(if_t ifp, int setcap, int clearcap) { if(setcap) ((struct ifnet *)ifp)->if_capenable |= setcap; if(clearcap) ((struct ifnet *)ifp)->if_capenable &= ~clearcap; return (0); } const char * if_getdname(if_t ifp) { return ((struct ifnet *)ifp)->if_dname; } int if_togglecapenable(if_t ifp, int togglecap) { ((struct ifnet *)ifp)->if_capenable ^= togglecap; return (0); } int if_getcapenable(if_t ifp) { return ((struct ifnet *)ifp)->if_capenable; } /* * This is largely undesirable because it ties ifnet to a device, but does * provide flexiblity for an embedded product vendor. Should be used with * the understanding that it violates the interface boundaries, and should be * a last resort only. */ int if_setdev(if_t ifp, void *dev) { return (0); } int if_setdrvflagbits(if_t ifp, int set_flags, int clear_flags) { ((struct ifnet *)ifp)->if_drv_flags |= set_flags; ((struct ifnet *)ifp)->if_drv_flags &= ~clear_flags; return (0); } int if_getdrvflags(if_t ifp) { return ((struct ifnet *)ifp)->if_drv_flags; } int if_setdrvflags(if_t ifp, int flags) { ((struct ifnet *)ifp)->if_drv_flags = flags; return (0); } int if_setflags(if_t ifp, int flags) { ((struct ifnet *)ifp)->if_flags = flags; return (0); } int if_setflagbits(if_t ifp, int set, int clear) { ((struct ifnet *)ifp)->if_flags |= set; ((struct ifnet *)ifp)->if_flags &= ~clear; return (0); } int if_getflags(if_t ifp) { return ((struct ifnet *)ifp)->if_flags; } int if_clearhwassist(if_t ifp) { ((struct ifnet *)ifp)->if_hwassist = 0; return (0); } int if_sethwassistbits(if_t ifp, int toset, int toclear) { ((struct ifnet *)ifp)->if_hwassist |= toset; ((struct ifnet *)ifp)->if_hwassist &= ~toclear; return (0); } int if_sethwassist(if_t ifp, int hwassist_bit) { ((struct ifnet *)ifp)->if_hwassist = hwassist_bit; return (0); } int if_gethwassist(if_t ifp) { return ((struct ifnet *)ifp)->if_hwassist; } int if_setmtu(if_t ifp, int mtu) { ((struct ifnet *)ifp)->if_mtu = mtu; return (0); } int if_getmtu(if_t ifp) { return ((struct ifnet *)ifp)->if_mtu; } int if_getmtu_family(if_t ifp, int family) { struct domain *dp; for (dp = domains; dp; dp = dp->dom_next) { if (dp->dom_family == family && dp->dom_ifmtu != NULL) return (dp->dom_ifmtu((struct ifnet *)ifp)); } return (((struct ifnet *)ifp)->if_mtu); } int if_setsoftc(if_t ifp, void *softc) { ((struct ifnet *)ifp)->if_softc = softc; return (0); } void * if_getsoftc(if_t ifp) { return ((struct ifnet *)ifp)->if_softc; } void if_setrcvif(struct mbuf *m, if_t ifp) { m->m_pkthdr.rcvif = (struct ifnet *)ifp; } void if_setvtag(struct mbuf *m, uint16_t tag) { m->m_pkthdr.ether_vtag = tag; } uint16_t if_getvtag(struct mbuf *m) { return (m->m_pkthdr.ether_vtag); } int if_sendq_empty(if_t ifp) { return IFQ_DRV_IS_EMPTY(&((struct ifnet *)ifp)->if_snd); } struct ifaddr * if_getifaddr(if_t ifp) { return ((struct ifnet *)ifp)->if_addr; } int if_getamcount(if_t ifp) { return ((struct ifnet *)ifp)->if_amcount; } int if_setsendqready(if_t ifp) { IFQ_SET_READY(&((struct ifnet *)ifp)->if_snd); return (0); } int if_setsendqlen(if_t ifp, int tx_desc_count) { IFQ_SET_MAXLEN(&((struct ifnet *)ifp)->if_snd, tx_desc_count); ((struct ifnet *)ifp)->if_snd.ifq_drv_maxlen = tx_desc_count; return (0); } int if_vlantrunkinuse(if_t ifp) { return ((struct ifnet *)ifp)->if_vlantrunk != NULL?1:0; } int if_input(if_t ifp, struct mbuf* sendmp) { (*((struct ifnet *)ifp)->if_input)((struct ifnet *)ifp, sendmp); return (0); } /* XXX */ #ifndef ETH_ADDR_LEN #define ETH_ADDR_LEN 6 #endif int if_setupmultiaddr(if_t ifp, void *mta, int *cnt, int max) { struct ifmultiaddr *ifma; uint8_t *lmta = (uint8_t *)mta; int mcnt = 0; TAILQ_FOREACH(ifma, &((struct ifnet *)ifp)->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (mcnt == max) break; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), &lmta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN); mcnt++; } *cnt = mcnt; return (0); } int if_multiaddr_array(if_t ifp, void *mta, int *cnt, int max) { int error; if_maddr_rlock(ifp); error = if_setupmultiaddr(ifp, mta, cnt, max); if_maddr_runlock(ifp); return (error); } int if_multiaddr_count(if_t ifp, int max) { struct ifmultiaddr *ifma; int count; count = 0; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &((struct ifnet *)ifp)->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; count++; if (count == max) break; } if_maddr_runlock(ifp); return (count); } struct mbuf * if_dequeue(if_t ifp) { struct mbuf *m; IFQ_DRV_DEQUEUE(&((struct ifnet *)ifp)->if_snd, m); return (m); } int if_sendq_prepend(if_t ifp, struct mbuf *m) { IFQ_DRV_PREPEND(&((struct ifnet *)ifp)->if_snd, m); return (0); } int if_setifheaderlen(if_t ifp, int len) { ((struct ifnet *)ifp)->if_hdrlen = len; return (0); } caddr_t if_getlladdr(if_t ifp) { return (IF_LLADDR((struct ifnet *)ifp)); } void * if_gethandle(u_char type) { return (if_alloc(type)); } void if_bpfmtap(if_t ifh, struct mbuf *m) { struct ifnet *ifp = (struct ifnet *)ifh; BPF_MTAP(ifp, m); } void if_etherbpfmtap(if_t ifh, struct mbuf *m) { struct ifnet *ifp = (struct ifnet *)ifh; ETHER_BPF_MTAP(ifp, m); } void if_vlancap(if_t ifh) { struct ifnet *ifp = (struct ifnet *)ifh; VLAN_CAPABILITIES(ifp); } void if_setinitfn(if_t ifp, void (*init_fn)(void *)) { ((struct ifnet *)ifp)->if_init = init_fn; } void if_setioctlfn(if_t ifp, int (*ioctl_fn)(if_t, u_long, caddr_t)) { ((struct ifnet *)ifp)->if_ioctl = (void *)ioctl_fn; } void if_setstartfn(if_t ifp, void (*start_fn)(if_t)) { ((struct ifnet *)ifp)->if_start = (void *)start_fn; } void if_settransmitfn(if_t ifp, if_transmit_fn_t start_fn) { ((struct ifnet *)ifp)->if_transmit = start_fn; } void if_setqflushfn(if_t ifp, if_qflush_fn_t flush_fn) { ((struct ifnet *)ifp)->if_qflush = flush_fn; } void if_setgetcounterfn(if_t ifp, if_get_counter_t fn) { ifp->if_get_counter = fn; } /* Revisit these - These are inline functions originally. */ int drbr_inuse_drv(if_t ifh, struct buf_ring *br) { return drbr_inuse(ifh, br); } struct mbuf* drbr_dequeue_drv(if_t ifh, struct buf_ring *br) { return drbr_dequeue(ifh, br); } int drbr_needs_enqueue_drv(if_t ifh, struct buf_ring *br) { return drbr_needs_enqueue(ifh, br); } int drbr_enqueue_drv(if_t ifh, struct buf_ring *br, struct mbuf *m) { return drbr_enqueue(ifh, br, m); } Index: projects/clang380-import/sys/net/if_ethersubr.c =================================================================== --- projects/clang380-import/sys/net/if_ethersubr.c (revision 293015) +++ projects/clang380-import/sys/net/if_ethersubr.c (revision 293016) @@ -1,1139 +1,1196 @@ /*- * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_netgraph.h" #include "opt_mbuf_profiling.h" #include "opt_rss.h" #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 #include #include #if defined(INET) || defined(INET6) #include #include #include #include #include #endif #ifdef INET6 #include #endif #include #ifdef CTASSERT CTASSERT(sizeof (struct ether_header) == ETHER_ADDR_LEN * 2 + 2); CTASSERT(sizeof (struct ether_addr) == ETHER_ADDR_LEN); #endif VNET_DEFINE(struct pfil_head, link_pfil_hook); /* Packet filter hooks */ /* netgraph node hooks for ng_ether(4) */ void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m); int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_attach_p)(struct ifnet *ifp); void (*ng_ether_detach_p)(struct ifnet *ifp); void (*vlan_input_p)(struct ifnet *, struct mbuf *); /* if_bridge(4) support */ struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); int (*bridge_output_p)(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); void (*bridge_dn_p)(struct mbuf *, struct ifnet *); /* if_lagg(4) support */ struct mbuf *(*lagg_input_p)(struct ifnet *, struct mbuf *); static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ether_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); #ifdef VIMAGE static void ether_reassign(struct ifnet *, struct vnet *, char *); #endif +static int ether_requestencap(struct ifnet *, struct if_encap_req *); #define ETHER_IS_BROADCAST(addr) \ (bcmp(etherbroadcastaddr, (addr), ETHER_ADDR_LEN) == 0) #define senderr(e) do { error = (e); goto bad;} while (0) static void update_mbuf_csumflags(struct mbuf *src, struct mbuf *dst) { int csum_flags = 0; if (src->m_pkthdr.csum_flags & CSUM_IP) csum_flags |= (CSUM_IP_CHECKED|CSUM_IP_VALID); if (src->m_pkthdr.csum_flags & CSUM_DELAY_DATA) csum_flags |= (CSUM_DATA_VALID|CSUM_PSEUDO_HDR); if (src->m_pkthdr.csum_flags & CSUM_SCTP) csum_flags |= CSUM_SCTP_VALID; dst->m_pkthdr.csum_flags |= csum_flags; if (csum_flags & CSUM_DATA_VALID) dst->m_pkthdr.csum_data = 0xffff; } /* + * Handle link-layer encapsulation requests. + */ +static int +ether_requestencap(struct ifnet *ifp, struct if_encap_req *req) +{ + struct ether_header *eh; + struct arphdr *ah; + uint16_t etype; + const u_char *lladdr; + + if (req->rtype != IFENCAP_LL) + return (EOPNOTSUPP); + + if (req->bufsize < ETHER_HDR_LEN) + return (ENOMEM); + + eh = (struct ether_header *)req->buf; + lladdr = req->lladdr; + req->lladdr_off = 0; + + switch (req->family) { + case AF_INET: + etype = htons(ETHERTYPE_IP); + break; + case AF_INET6: + etype = htons(ETHERTYPE_IPV6); + break; + case AF_ARP: + ah = (struct arphdr *)req->hdata; + ah->ar_hrd = htons(ARPHRD_ETHER); + + switch(ntohs(ah->ar_op)) { + case ARPOP_REVREQUEST: + case ARPOP_REVREPLY: + etype = htons(ETHERTYPE_REVARP); + break; + case ARPOP_REQUEST: + case ARPOP_REPLY: + default: + etype = htons(ETHERTYPE_ARP); + break; + } + + if (req->flags & IFENCAP_FLAG_BROADCAST) + lladdr = ifp->if_broadcastaddr; + break; + default: + return (EAFNOSUPPORT); + } + + memcpy(&eh->ether_type, &etype, sizeof(eh->ether_type)); + memcpy(eh->ether_dhost, lladdr, ETHER_ADDR_LEN); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + req->bufsize = sizeof(struct ether_header); + + return (0); +} + + +static int +ether_resolve_addr(struct ifnet *ifp, struct mbuf *m, + const struct sockaddr *dst, struct route *ro, u_char *phdr, + uint32_t *pflags) +{ + struct ether_header *eh; + struct rtentry *rt; + uint32_t lleflags = 0; + int error = 0; +#if defined(INET) || defined(INET6) + uint16_t etype; +#endif + + eh = (struct ether_header *)phdr; + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) + error = arpresolve(ifp, 0, m, dst, phdr, &lleflags); + else { + if (m->m_flags & M_BCAST) + memcpy(eh->ether_dhost, ifp->if_broadcastaddr, + ETHER_ADDR_LEN); + else { + const struct in_addr *a; + a = &(((const struct sockaddr_in *)dst)->sin_addr); + ETHER_MAP_IP_MULTICAST(a, eh->ether_dhost); + } + etype = htons(ETHERTYPE_IP); + memcpy(&eh->ether_type, &etype, sizeof(etype)); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + } + break; +#endif +#ifdef INET6 + case AF_INET6: + if ((m->m_flags & M_MCAST) == 0) + error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags); + else { + const struct in6_addr *a6; + a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr); + ETHER_MAP_IPV6_MULTICAST(a6, eh->ether_dhost); + etype = htons(ETHERTYPE_IPV6); + memcpy(&eh->ether_type, &etype, sizeof(etype)); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + } + break; +#endif + default: + if_printf(ifp, "can't handle af%d\n", dst->sa_family); + if (m != NULL) + m_freem(m); + return (EAFNOSUPPORT); + } + + if (error == EHOSTDOWN) { + rt = (ro != NULL) ? ro->ro_rt : NULL; + if (rt != NULL && (rt->rt_flags & RTF_GATEWAY) != 0) + error = EHOSTUNREACH; + } + + if (error != 0) + return (error); + + *pflags = RT_MAY_LOOP; + if (lleflags & LLE_IFADDR) + *pflags |= RT_L2_ME; + + return (0); +} + +/* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. */ int ether_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { - short type; - int error = 0, hdrcmplt = 0; - u_char edst[ETHER_ADDR_LEN]; - struct llentry *lle = NULL; - struct rtentry *rt0 = NULL; + int error = 0; + char linkhdr[ETHER_HDR_LEN], *phdr; struct ether_header *eh; struct pf_mtag *t; int loop_copy = 1; int hlen; /* link layer header length */ - int is_gw = 0; - uint32_t pflags = 0; + uint32_t pflags; + phdr = NULL; + pflags = 0; if (ro != NULL) { - if (!(m->m_flags & (M_BCAST | M_MCAST))) { - lle = ro->ro_lle; - if (lle != NULL) - pflags = lle->la_flags; - } - rt0 = ro->ro_rt; - if (rt0 != NULL && (rt0->rt_flags & RTF_GATEWAY) != 0) - is_gw = 1; + phdr = ro->ro_prepend; + hlen = ro->ro_plen; + pflags = ro->ro_flags; } #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) senderr(error); #endif M_PROFILE(m); if (ifp->if_flags & IFF_MONITOR) senderr(ENETDOWN); if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) senderr(ENETDOWN); - hlen = ETHER_HDR_LEN; - switch (dst->sa_family) { -#ifdef INET - case AF_INET: - if (lle != NULL && (pflags & LLE_VALID) != 0) - memcpy(edst, &lle->ll_addr.mac16, sizeof(edst)); - else - error = arpresolve(ifp, is_gw, m, dst, edst, &pflags); - if (error) + if (phdr == NULL) { + /* No prepend data supplied. Try to calculate ourselves. */ + phdr = linkhdr; + hlen = ETHER_HDR_LEN; + error = ether_resolve_addr(ifp, m, dst, ro, phdr, &pflags); + if (error != 0) return (error == EWOULDBLOCK ? 0 : error); - type = htons(ETHERTYPE_IP); - break; - case AF_ARP: - { - struct arphdr *ah; - ah = mtod(m, struct arphdr *); - ah->ar_hrd = htons(ARPHRD_ETHER); - - loop_copy = 0; /* if this is for us, don't do it */ - - switch(ntohs(ah->ar_op)) { - case ARPOP_REVREQUEST: - case ARPOP_REVREPLY: - type = htons(ETHERTYPE_REVARP); - break; - case ARPOP_REQUEST: - case ARPOP_REPLY: - default: - type = htons(ETHERTYPE_ARP); - break; - } - - if (m->m_flags & M_BCAST) - bcopy(ifp->if_broadcastaddr, edst, ETHER_ADDR_LEN); - else - bcopy(ar_tha(ah), edst, ETHER_ADDR_LEN); - } - break; -#endif -#ifdef INET6 - case AF_INET6: - if (lle != NULL && (pflags & LLE_VALID)) - memcpy(edst, &lle->ll_addr.mac16, sizeof(edst)); - else - error = nd6_resolve(ifp, is_gw, m, dst, (u_char *)edst, - &pflags); - if (error) - return (error == EWOULDBLOCK ? 0 : error); - type = htons(ETHERTYPE_IPV6); - break; -#endif - case pseudo_AF_HDRCMPLT: - { - const struct ether_header *eh; - hdrcmplt = 1; - /* FALLTHROUGH */ - - case AF_UNSPEC: - loop_copy = 0; /* if this is for us, don't do it */ - eh = (const struct ether_header *)dst->sa_data; - (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); - type = eh->ether_type; - break; - } - default: - if_printf(ifp, "can't handle af%d\n", dst->sa_family); - senderr(EAFNOSUPPORT); - } - - if ((pflags & LLE_IFADDR) != 0) { + if ((pflags & RT_L2_ME) != 0) { update_mbuf_csumflags(m, m); return (if_simloop(ifp, m, dst->sa_family, 0)); } + loop_copy = pflags & RT_MAY_LOOP; /* * Add local net header. If no space in first mbuf, * allocate another. */ - M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + M_PREPEND(m, hlen, M_NOWAIT); if (m == NULL) senderr(ENOBUFS); - eh = mtod(m, struct ether_header *); - if (hdrcmplt == 0) { - memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); - memcpy(eh->ether_dhost, edst, sizeof (edst)); - memcpy(eh->ether_shost, IF_LLADDR(ifp),sizeof(eh->ether_shost)); + if ((pflags & RT_HAS_HEADER) == 0) { + eh = mtod(m, struct ether_header *); + memcpy(eh, phdr, hlen); } /* * If a simplex interface, and the packet is being sent to our * Ethernet address or a broadcast address, loopback a copy. * XXX To make a simplex device behave exactly like a duplex * device, we should copy in the case of sending to our own * ethernet address (thus letting the original actually appear * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ - if ((ifp->if_flags & IFF_SIMPLEX) && loop_copy && + if ((m->m_flags & M_BCAST) && loop_copy && (ifp->if_flags & IFF_SIMPLEX) && ((t = pf_find_mtag(m)) == NULL || !t->routed)) { - if (m->m_flags & M_BCAST) { - struct mbuf *n; + struct mbuf *n; - /* - * Because if_simloop() modifies the packet, we need a - * writable copy through m_dup() instead of a readonly - * one as m_copy[m] would give us. The alternative would - * be to modify if_simloop() to handle the readonly mbuf, - * but performancewise it is mostly equivalent (trading - * extra data copying vs. extra locking). - * - * XXX This is a local workaround. A number of less - * often used kernel parts suffer from the same bug. - * See PR kern/105943 for a proposed general solution. - */ - if ((n = m_dup(m, M_NOWAIT)) != NULL) { - update_mbuf_csumflags(m, n); - (void)if_simloop(ifp, n, dst->sa_family, hlen); - } else - if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); - } else if (bcmp(eh->ether_dhost, eh->ether_shost, - ETHER_ADDR_LEN) == 0) { - update_mbuf_csumflags(m, m); - (void) if_simloop(ifp, m, dst->sa_family, hlen); - return (0); /* XXX */ - } + /* + * Because if_simloop() modifies the packet, we need a + * writable copy through m_dup() instead of a readonly + * one as m_copy[m] would give us. The alternative would + * be to modify if_simloop() to handle the readonly mbuf, + * but performancewise it is mostly equivalent (trading + * extra data copying vs. extra locking). + * + * XXX This is a local workaround. A number of less + * often used kernel parts suffer from the same bug. + * See PR kern/105943 for a proposed general solution. + */ + if ((n = m_dup(m, M_NOWAIT)) != NULL) { + update_mbuf_csumflags(m, n); + (void)if_simloop(ifp, n, dst->sa_family, hlen); + } else + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); } /* * Bridges require special output handling. */ if (ifp->if_bridge) { BRIDGE_OUTPUT(ifp, m, error); return (error); } #if defined(INET) || defined(INET6) if (ifp->if_carp && (error = (*carp_output_p)(ifp, m, dst))) goto bad; #endif /* Handle ng_ether(4) processing, if any */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_output_p != NULL, ("ng_ether_output_p is NULL")); if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* Continue with link-layer output */ return ether_output_frame(ifp, m); } /* * Ethernet link layer output routine to send a raw frame to the device. * * This assumes that the 14 byte Ethernet header is present and contiguous * in the first mbuf (if BRIDGE'ing). */ int ether_output_frame(struct ifnet *ifp, struct mbuf *m) { int i; if (PFIL_HOOKED(&V_link_pfil_hook)) { i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_OUT, NULL); if (i != 0) return (EACCES); if (m == NULL) return (0); } /* * Queue message on interface, update output statistics if * successful, and start output if interface not yet active. */ return ((ifp->if_transmit)(ifp, m)); } #if defined(INET) || defined(INET6) #endif /* * Process a received Ethernet packet; the packet is in the * mbuf chain m with the ethernet header at the front. */ static void ether_input_internal(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; u_short etype; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } #ifdef DIAGNOSTIC if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { if_printf(ifp, "discard frame at !IFF_DRV_RUNNING\n"); m_freem(m); return; } #endif if (m->m_len < ETHER_HDR_LEN) { /* XXX maybe should pullup? */ if_printf(ifp, "discard frame w/o leading ethernet " "header (len %u pkt len %u)\n", m->m_len, m->m_pkthdr.len); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); return; } eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); random_harvest_queue(m, sizeof(*m), 2, RANDOM_NET_ETHER); CURVNET_SET_QUIET(ifp->if_vnet); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (ETHER_IS_BROADCAST(eh->ether_dhost)) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); } #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_ifnet_create_mbuf(ifp, m); #endif /* * Give bpf a chance at the packet. */ ETHER_BPF_MTAP(ifp, m); /* * If the CRC is still on the packet, trim it off. We do this once * and once only in case we are re-entered. Nothing else on the * Ethernet receive path expects to see the FCS. */ if (m->m_flags & M_HASFCS) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } if (!(ifp->if_capenable & IFCAP_HWSTATS)) if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); /* Allow monitor mode to claim this frame, after stats are updated. */ if (ifp->if_flags & IFF_MONITOR) { m_freem(m); CURVNET_RESTORE(); return; } /* Handle input from a lagg(4) port */ if (ifp->if_type == IFT_IEEE8023ADLAG) { KASSERT(lagg_input_p != NULL, ("%s: if_lagg not loaded!", __func__)); m = (*lagg_input_p)(ifp, m); if (m != NULL) ifp = m->m_pkthdr.rcvif; else { CURVNET_RESTORE(); return; } } /* * If the hardware did not process an 802.1Q tag, do this now, * to allow 802.1P priority frames to be passed to the main input * path correctly. * TODO: Deal with Q-in-Q frames, but not arbitrary nesting levels. */ if ((m->m_flags & M_VLANTAG) == 0 && etype == ETHERTYPE_VLAN) { struct ether_vlan_header *evl; if (m->m_len < sizeof(*evl) && (m = m_pullup(m, sizeof(*evl))) == NULL) { #ifdef DIAGNOSTIC if_printf(ifp, "cannot pullup VLAN header\n"); #endif if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); CURVNET_RESTORE(); return; } evl = mtod(m, struct ether_vlan_header *); m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); m->m_flags |= M_VLANTAG; bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, ETHER_HDR_LEN - ETHER_TYPE_LEN); m_adj(m, ETHER_VLAN_ENCAP_LEN); eh = mtod(m, struct ether_header *); } M_SETFIB(m, ifp->if_fib); /* Allow ng_ether(4) to claim this frame. */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_input_p != NULL, ("%s: ng_ether_input_p is NULL", __func__)); m->m_flags &= ~M_PROMISC; (*ng_ether_input_p)(ifp, &m); if (m == NULL) { CURVNET_RESTORE(); return; } eh = mtod(m, struct ether_header *); } /* * Allow if_bridge(4) to claim this frame. * The BRIDGE_INPUT() macro will update ifp if the bridge changed it * and the frame should be delivered locally. */ if (ifp->if_bridge != NULL) { m->m_flags &= ~M_PROMISC; BRIDGE_INPUT(ifp, m); if (m == NULL) { CURVNET_RESTORE(); return; } eh = mtod(m, struct ether_header *); } #if defined(INET) || defined(INET6) /* * Clear M_PROMISC on frame so that carp(4) will see it when the * mbuf flows up to Layer 3. * FreeBSD's implementation of carp(4) uses the inprotosw * to dispatch IPPROTO_CARP. carp(4) also allocates its own * Ethernet addresses of the form 00:00:5e:00:01:xx, which * is outside the scope of the M_PROMISC test below. * TODO: Maintain a hash table of ethernet addresses other than * ether_dhost which may be active on this ifp. */ if (ifp->if_carp && (*carp_forus_p)(ifp, eh->ether_dhost)) { m->m_flags &= ~M_PROMISC; } else #endif { /* * If the frame received was not for our MAC address, set the * M_PROMISC flag on the mbuf chain. The frame may need to * be seen by the rest of the Ethernet input path in case of * re-entry (e.g. bridge, vlan, netgraph) but should not be * seen by upper protocol layers. */ if (!ETHER_IS_MULTICAST(eh->ether_dhost) && bcmp(IF_LLADDR(ifp), eh->ether_dhost, ETHER_ADDR_LEN) != 0) m->m_flags |= M_PROMISC; } ether_demux(ifp, m); CURVNET_RESTORE(); } /* * Ethernet input dispatch; by default, direct dispatch here regardless of * global configuration. However, if RSS is enabled, hook up RSS affinity * so that when deferred or hybrid dispatch is enabled, we can redistribute * load based on RSS. * * XXXRW: Would be nice if the ifnet passed up a flag indicating whether or * not it had already done work distribution via multi-queue. Then we could * direct dispatch in the event load balancing was already complete and * handle the case of interfaces with different capabilities better. * * XXXRW: Sort of want an M_DISTRIBUTED flag to avoid multiple distributions * at multiple layers? * * XXXRW: For now, enable all this only if RSS is compiled in, although it * works fine without RSS. Need to characterise the performance overhead * of the detour through the netisr code in the event the result is always * direct dispatch. */ static void ether_nh_input(struct mbuf *m) { M_ASSERTPKTHDR(m); KASSERT(m->m_pkthdr.rcvif != NULL, ("%s: NULL interface pointer", __func__)); ether_input_internal(m->m_pkthdr.rcvif, m); } static struct netisr_handler ether_nh = { .nh_name = "ether", .nh_handler = ether_nh_input, .nh_proto = NETISR_ETHER, #ifdef RSS .nh_policy = NETISR_POLICY_CPU, .nh_dispatch = NETISR_DISPATCH_DIRECT, .nh_m2cpuid = rss_m2cpuid, #else .nh_policy = NETISR_POLICY_SOURCE, .nh_dispatch = NETISR_DISPATCH_DIRECT, #endif }; static void ether_init(__unused void *arg) { netisr_register(ðer_nh); } SYSINIT(ether, SI_SUB_INIT_IF, SI_ORDER_ANY, ether_init, NULL); static void vnet_ether_init(__unused void *arg) { int i; /* Initialize packet filter hooks. */ V_link_pfil_hook.ph_type = PFIL_TYPE_AF; V_link_pfil_hook.ph_af = AF_LINK; if ((i = pfil_head_register(&V_link_pfil_hook)) != 0) printf("%s: WARNING: unable to register pfil link hook, " "error %d\n", __func__, i); } VNET_SYSINIT(vnet_ether_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_ether_init, NULL); static void vnet_ether_destroy(__unused void *arg) { int i; if ((i = pfil_head_unregister(&V_link_pfil_hook)) != 0) printf("%s: WARNING: unable to unregister pfil link hook, " "error %d\n", __func__, i); } VNET_SYSUNINIT(vnet_ether_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, vnet_ether_destroy, NULL); static void ether_input(struct ifnet *ifp, struct mbuf *m) { struct mbuf *mn; /* * The drivers are allowed to pass in a chain of packets linked with * m_nextpkt. We split them up into separate packets here and pass * them up. This allows the drivers to amortize the receive lock. */ while (m) { mn = m->m_nextpkt; m->m_nextpkt = NULL; /* * We will rely on rcvif being set properly in the deferred context, * so assert it is correct here. */ KASSERT(m->m_pkthdr.rcvif == ifp, ("%s: ifnet mismatch", __func__)); netisr_dispatch(NETISR_ETHER, m); m = mn; } } /* * Upper layer processing for a received Ethernet packet. */ void ether_demux(struct ifnet *ifp, struct mbuf *m) { struct ether_header *eh; int i, isr; u_short ether_type; KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__)); /* Do not grab PROMISC frames in case we are re-entered. */ if (PFIL_HOOKED(&V_link_pfil_hook) && !(m->m_flags & M_PROMISC)) { i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_IN, NULL); if (i != 0 || m == NULL) return; } eh = mtod(m, struct ether_header *); ether_type = ntohs(eh->ether_type); /* * If this frame has a VLAN tag other than 0, call vlan_input() * if its module is loaded. Otherwise, drop. */ if ((m->m_flags & M_VLANTAG) && EVL_VLANOFTAG(m->m_pkthdr.ether_vtag) != 0) { if (ifp->if_vlantrunk == NULL) { if_inc_counter(ifp, IFCOUNTER_NOPROTO, 1); m_freem(m); return; } KASSERT(vlan_input_p != NULL,("%s: VLAN not loaded!", __func__)); /* Clear before possibly re-entering ether_input(). */ m->m_flags &= ~M_PROMISC; (*vlan_input_p)(ifp, m); return; } /* * Pass promiscuously received frames to the upper layer if the user * requested this by setting IFF_PPROMISC. Otherwise, drop them. */ if ((ifp->if_flags & IFF_PPROMISC) == 0 && (m->m_flags & M_PROMISC)) { m_freem(m); return; } /* * Reset layer specific mbuf flags to avoid confusing upper layers. * Strip off Ethernet header. */ m->m_flags &= ~M_VLANTAG; m_clrprotoflags(m); m_adj(m, ETHER_HDR_LEN); /* * Dispatch frame to upper layer. */ switch (ether_type) { #ifdef INET case ETHERTYPE_IP: isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } isr = NETISR_ARP; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif default: goto discard; } netisr_dispatch(isr, m); return; discard: /* * Packet is to be discarded. If netgraph is present, * hand the packet to it for last chance processing; * otherwise dispose of it. */ if (ifp->if_l2com != NULL) { KASSERT(ng_ether_input_orphan_p != NULL, ("ng_ether_input_orphan_p is NULL")); /* * Put back the ethernet header so netgraph has a * consistent view of inbound packets. */ M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); (*ng_ether_input_orphan_p)(ifp, m); return; } m_freem(m); } /* * Convert Ethernet address to printable (loggable) representation. * This routine is for compatibility; it's better to just use * * printf("%6D", , ":"); * * since there's no static buffer involved. */ char * ether_sprintf(const u_char *ap) { static char etherbuf[18]; snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":"); return (etherbuf); } /* * Perform common duties while attaching to interface list */ void ether_ifattach(struct ifnet *ifp, const u_int8_t *lla) { int i; struct ifaddr *ifa; struct sockaddr_dl *sdl; ifp->if_addrlen = ETHER_ADDR_LEN; ifp->if_hdrlen = ETHER_HDR_LEN; if_attach(ifp); ifp->if_mtu = ETHERMTU; ifp->if_output = ether_output; ifp->if_input = ether_input; ifp->if_resolvemulti = ether_resolvemulti; + ifp->if_requestencap = ether_requestencap; #ifdef VIMAGE ifp->if_reassign = ether_reassign; #endif if (ifp->if_baudrate == 0) ifp->if_baudrate = IF_Mbps(10); /* just a default */ ifp->if_broadcastaddr = etherbroadcastaddr; ifa = ifp->if_addr; KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__)); sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy(lla, LLADDR(sdl), ifp->if_addrlen); bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN); if (ng_ether_attach_p != NULL) (*ng_ether_attach_p)(ifp); /* Announce Ethernet MAC address if non-zero. */ for (i = 0; i < ifp->if_addrlen; i++) if (lla[i] != 0) break; if (i != ifp->if_addrlen) if_printf(ifp, "Ethernet address: %6D\n", lla, ":"); uuid_ether_add(LLADDR(sdl)); } /* * Perform common duties while detaching an Ethernet interface */ void ether_ifdetach(struct ifnet *ifp) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)(ifp->if_addr->ifa_addr); uuid_ether_del(LLADDR(sdl)); if (ifp->if_l2com != NULL) { KASSERT(ng_ether_detach_p != NULL, ("ng_ether_detach_p is NULL")); (*ng_ether_detach_p)(ifp); } bpfdetach(ifp); if_detach(ifp); } #ifdef VIMAGE void ether_reassign(struct ifnet *ifp, struct vnet *new_vnet, char *unused __unused) { if (ifp->if_l2com != NULL) { KASSERT(ng_ether_detach_p != NULL, ("ng_ether_detach_p is NULL")); (*ng_ether_detach_p)(ifp); } if (ng_ether_attach_p != NULL) { CURVNET_SET_QUIET(new_vnet); (*ng_ether_attach_p)(ifp); CURVNET_RESTORE(); } } #endif SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); #if 0 /* * This is for reference. We have a table-driven version * of the little-endian crc32 generator, which is faster * than the double-loop. */ uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { size_t i; uint32_t crc; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = (crc ^ data) & 1; crc >>= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_LE); } } return (crc); } #else uint32_t ether_crc32_le(const uint8_t *buf, size_t len) { static const uint32_t crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; size_t i; uint32_t crc; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { crc ^= buf[i]; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return (crc); } #endif uint32_t ether_crc32_be(const uint8_t *buf, size_t len) { size_t i; uint32_t crc, carry; int bit; uint8_t data; crc = 0xffffffff; /* initial value */ for (i = 0; i < len; i++) { for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); crc <<= 1; if (carry) crc = (crc ^ ETHER_CRC_POLY_BE) | carry; } } return (crc); } int ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IF_LLADDR(ifp), (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; default: error = EINVAL; /* XXX netbsd has ENOTTY??? */ break; } return (error); } static int ether_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa) { struct sockaddr_dl *sdl; #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if (!ETHER_IS_MULTICAST(e_addr)) return EADDRNOTAVAIL; *llsa = 0; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_ETHER); sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* * An IP6 address of 0 means listen to all * of the Ethernet multicast address used for IP6. * (This is used for multicast routers.) */ ifp->if_flags |= IFF_ALLMULTI; *llsa = 0; return 0; } if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_ETHER); sdl->sdl_alen = ETHER_ADDR_LEN; e_addr = LLADDR(sdl); ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } static moduledata_t ether_mod = { .name = "ether", }; void ether_vlan_mtap(struct bpf_if *bp, struct mbuf *m, void *data, u_int dlen) { struct ether_vlan_header vlan; struct mbuf mv, mb; KASSERT((m->m_flags & M_VLANTAG) != 0, ("%s: vlan information not present", __func__)); KASSERT(m->m_len >= sizeof(struct ether_header), ("%s: mbuf not large enough for header", __func__)); bcopy(mtod(m, char *), &vlan, sizeof(struct ether_header)); vlan.evl_proto = vlan.evl_encap_proto; vlan.evl_encap_proto = htons(ETHERTYPE_VLAN); vlan.evl_tag = htons(m->m_pkthdr.ether_vtag); m->m_len -= sizeof(struct ether_header); m->m_data += sizeof(struct ether_header); /* * If a data link has been supplied by the caller, then we will need to * re-create a stack allocated mbuf chain with the following structure: * * (1) mbuf #1 will contain the supplied data link * (2) mbuf #2 will contain the vlan header * (3) mbuf #3 will contain the original mbuf's packet data * * Otherwise, submit the packet and vlan header via bpf_mtap2(). */ if (data != NULL) { mv.m_next = m; mv.m_data = (caddr_t)&vlan; mv.m_len = sizeof(vlan); mb.m_next = &mv; mb.m_data = data; mb.m_len = dlen; bpf_mtap(bp, &mb); } else bpf_mtap2(bp, &vlan, sizeof(vlan), m); m->m_len += sizeof(struct ether_header); m->m_data -= sizeof(struct ether_header); } struct mbuf * ether_vlanencap(struct mbuf *m, uint16_t tag) { struct ether_vlan_header *evl; M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT); if (m == NULL) return (NULL); /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ if (m->m_len < sizeof(*evl)) { m = m_pullup(m, sizeof(*evl)); if (m == NULL) return (NULL); } /* * Transform the Ethernet header into an Ethernet header * with 802.1Q encapsulation. */ evl = mtod(m, struct ether_vlan_header *); bcopy((char *)evl + ETHER_VLAN_ENCAP_LEN, (char *)evl, ETHER_HDR_LEN - ETHER_TYPE_LEN); evl->evl_encap_proto = htons(ETHERTYPE_VLAN); evl->evl_tag = htons(tag); return (m); } DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY); MODULE_VERSION(ether, 1); Index: projects/clang380-import/sys/net/if_gif.c =================================================================== --- projects/clang380-import/sys/net/if_gif.c (revision 293015) +++ projects/clang380-import/sys/net/if_gif.c (revision 293016) @@ -1,1074 +1,1072 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: if_gif.c,v 1.87 2001/10/19 08:50:27 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #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 #include #include #include #include #include #include #ifdef INET #include #include #endif /* INET */ #ifdef INET6 #ifndef INET #include #endif #include #include #include #include #include #include #endif /* INET6 */ #include #include #include #include #include static const char gifname[] = "gif"; /* * gif_mtx protects a per-vnet gif_softc_list. */ static VNET_DEFINE(struct mtx, gif_mtx); #define V_gif_mtx VNET(gif_mtx) static MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface"); static VNET_DEFINE(LIST_HEAD(, gif_softc), gif_softc_list); #define V_gif_softc_list VNET(gif_softc_list) static struct sx gif_ioctl_sx; SX_SYSINIT(gif_ioctl_sx, &gif_ioctl_sx, "gif_ioctl"); #define GIF_LIST_LOCK_INIT(x) mtx_init(&V_gif_mtx, "gif_mtx", \ NULL, MTX_DEF) #define GIF_LIST_LOCK_DESTROY(x) mtx_destroy(&V_gif_mtx) #define GIF_LIST_LOCK(x) mtx_lock(&V_gif_mtx) #define GIF_LIST_UNLOCK(x) mtx_unlock(&V_gif_mtx) void (*ng_gif_input_p)(struct ifnet *ifp, struct mbuf **mp, int af); void (*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af); void (*ng_gif_attach_p)(struct ifnet *ifp); void (*ng_gif_detach_p)(struct ifnet *ifp); static int gif_check_nesting(struct ifnet *, struct mbuf *); static int gif_set_tunnel(struct ifnet *, struct sockaddr *, struct sockaddr *); static void gif_delete_tunnel(struct ifnet *); static int gif_ioctl(struct ifnet *, u_long, caddr_t); static int gif_transmit(struct ifnet *, struct mbuf *); static void gif_qflush(struct ifnet *); static int gif_clone_create(struct if_clone *, int, caddr_t); static void gif_clone_destroy(struct ifnet *); static VNET_DEFINE(struct if_clone *, gif_cloner); #define V_gif_cloner VNET(gif_cloner) static int gifmodevent(module_t, int, void *); SYSCTL_DECL(_net_link); static SYSCTL_NODE(_net_link, IFT_GIF, gif, CTLFLAG_RW, 0, "Generic Tunnel Interface"); #ifndef MAX_GIF_NEST /* * This macro controls the default upper limitation on nesting of gif tunnels. * Since, setting a large value to this macro with a careless configuration * may introduce system crash, we don't allow any nestings by default. * If you need to configure nested gif tunnels, you can define this macro * in your kernel configuration file. However, if you do so, please be * careful to configure the tunnels so that it won't make a loop. */ #define MAX_GIF_NEST 1 #endif static VNET_DEFINE(int, max_gif_nesting) = MAX_GIF_NEST; #define V_max_gif_nesting VNET(max_gif_nesting) SYSCTL_INT(_net_link_gif, OID_AUTO, max_nesting, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(max_gif_nesting), 0, "Max nested tunnels"); /* * By default, we disallow creation of multiple tunnels between the same * pair of addresses. Some applications require this functionality so * we allow control over this check here. */ #ifdef XBONEHACK static VNET_DEFINE(int, parallel_tunnels) = 1; #else static VNET_DEFINE(int, parallel_tunnels) = 0; #endif #define V_parallel_tunnels VNET(parallel_tunnels) SYSCTL_INT(_net_link_gif, OID_AUTO, parallel_tunnels, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(parallel_tunnels), 0, "Allow parallel tunnels?"); /* copy from src/sys/net/if_ethersubr.c */ static const u_char etherbroadcastaddr[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #ifndef ETHER_IS_BROADCAST #define ETHER_IS_BROADCAST(addr) \ (bcmp(etherbroadcastaddr, (addr), ETHER_ADDR_LEN) == 0) #endif static int gif_clone_create(struct if_clone *ifc, int unit, caddr_t params) { struct gif_softc *sc; sc = malloc(sizeof(struct gif_softc), M_GIF, M_WAITOK | M_ZERO); sc->gif_fibnum = curthread->td_proc->p_fibnum; GIF2IFP(sc) = if_alloc(IFT_GIF); GIF_LOCK_INIT(sc); GIF2IFP(sc)->if_softc = sc; if_initname(GIF2IFP(sc), gifname, unit); GIF2IFP(sc)->if_addrlen = 0; GIF2IFP(sc)->if_mtu = GIF_MTU; GIF2IFP(sc)->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; #if 0 /* turn off ingress filter */ GIF2IFP(sc)->if_flags |= IFF_LINK2; #endif GIF2IFP(sc)->if_ioctl = gif_ioctl; GIF2IFP(sc)->if_transmit = gif_transmit; GIF2IFP(sc)->if_qflush = gif_qflush; GIF2IFP(sc)->if_output = gif_output; GIF2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; GIF2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; if_attach(GIF2IFP(sc)); bpfattach(GIF2IFP(sc), DLT_NULL, sizeof(u_int32_t)); if (ng_gif_attach_p != NULL) (*ng_gif_attach_p)(GIF2IFP(sc)); GIF_LIST_LOCK(); LIST_INSERT_HEAD(&V_gif_softc_list, sc, gif_list); GIF_LIST_UNLOCK(); return (0); } static void gif_clone_destroy(struct ifnet *ifp) { struct gif_softc *sc; sx_xlock(&gif_ioctl_sx); sc = ifp->if_softc; gif_delete_tunnel(ifp); GIF_LIST_LOCK(); LIST_REMOVE(sc, gif_list); GIF_LIST_UNLOCK(); if (ng_gif_detach_p != NULL) (*ng_gif_detach_p)(ifp); bpfdetach(ifp); if_detach(ifp); ifp->if_softc = NULL; sx_xunlock(&gif_ioctl_sx); if_free(ifp); GIF_LOCK_DESTROY(sc); free(sc, M_GIF); } static void vnet_gif_init(const void *unused __unused) { LIST_INIT(&V_gif_softc_list); GIF_LIST_LOCK_INIT(); V_gif_cloner = if_clone_simple(gifname, gif_clone_create, gif_clone_destroy, 0); } VNET_SYSINIT(vnet_gif_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_gif_init, NULL); static void vnet_gif_uninit(const void *unused __unused) { if_clone_detach(V_gif_cloner); GIF_LIST_LOCK_DESTROY(); } VNET_SYSUNINIT(vnet_gif_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_gif_uninit, NULL); static int gifmodevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: case MOD_UNLOAD: break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t gif_mod = { "if_gif", gifmodevent, 0 }; DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(if_gif, 1); int gif_encapcheck(const struct mbuf *m, int off, int proto, void *arg) { GIF_RLOCK_TRACKER; const struct ip *ip; struct gif_softc *sc; int ret; sc = (struct gif_softc *)arg; if (sc == NULL || (GIF2IFP(sc)->if_flags & IFF_UP) == 0) return (0); ret = 0; GIF_RLOCK(sc); /* no physical address */ if (sc->gif_family == 0) goto done; switch (proto) { #ifdef INET case IPPROTO_IPV4: #endif #ifdef INET6 case IPPROTO_IPV6: #endif case IPPROTO_ETHERIP: break; default: goto done; } /* Bail on short packets */ M_ASSERTPKTHDR(m); if (m->m_pkthdr.len < sizeof(struct ip)) goto done; ip = mtod(m, const struct ip *); switch (ip->ip_v) { #ifdef INET case 4: if (sc->gif_family != AF_INET) goto done; ret = in_gif_encapcheck(m, off, proto, arg); break; #endif #ifdef INET6 case 6: if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) goto done; if (sc->gif_family != AF_INET6) goto done; ret = in6_gif_encapcheck(m, off, proto, arg); break; #endif } done: GIF_RUNLOCK(sc); return (ret); } static int gif_transmit(struct ifnet *ifp, struct mbuf *m) { struct gif_softc *sc; struct etherip_header *eth; #ifdef INET struct ip *ip; #endif #ifdef INET6 struct ip6_hdr *ip6; uint32_t t; #endif uint32_t af; uint8_t proto, ecn; int error; #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) { m_freem(m); goto err; } #endif error = ENETDOWN; sc = ifp->if_softc; if ((ifp->if_flags & IFF_MONITOR) != 0 || (ifp->if_flags & IFF_UP) == 0 || sc->gif_family == 0 || (error = gif_check_nesting(ifp, m)) != 0) { m_freem(m); goto err; } /* Now pull back the af that we stashed in the csum_data. */ if (ifp->if_bridge) af = AF_LINK; else af = m->m_pkthdr.csum_data; m->m_flags &= ~(M_BCAST|M_MCAST); M_SETFIB(m, sc->gif_fibnum); BPF_MTAP2(ifp, &af, sizeof(af), m); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); /* inner AF-specific encapsulation */ ecn = 0; switch (af) { #ifdef INET case AF_INET: proto = IPPROTO_IPV4; if (m->m_len < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); if (m == NULL) { error = ENOBUFS; goto err; } ip = mtod(m, struct ip *); ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED: ECN_NOCARE, &ecn, &ip->ip_tos); break; #endif #ifdef INET6 case AF_INET6: proto = IPPROTO_IPV6; if (m->m_len < sizeof(struct ip6_hdr)) m = m_pullup(m, sizeof(struct ip6_hdr)); if (m == NULL) { error = ENOBUFS; goto err; } t = 0; ip6 = mtod(m, struct ip6_hdr *); ip6_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED: ECN_NOCARE, &t, &ip6->ip6_flow); ecn = (ntohl(t) >> 20) & 0xff; break; #endif case AF_LINK: proto = IPPROTO_ETHERIP; M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT); if (m == NULL) { error = ENOBUFS; goto err; } eth = mtod(m, struct etherip_header *); eth->eip_resvh = 0; eth->eip_ver = ETHERIP_VERSION; eth->eip_resvl = 0; break; default: error = EAFNOSUPPORT; m_freem(m); goto err; } /* XXX should we check if our outer source is legal? */ /* dispatch to output logic based on outer AF */ switch (sc->gif_family) { #ifdef INET case AF_INET: error = in_gif_output(ifp, m, proto, ecn); break; #endif #ifdef INET6 case AF_INET6: error = in6_gif_output(ifp, m, proto, ecn); break; #endif default: m_freem(m); } err: if (error) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (error); } static void gif_qflush(struct ifnet *ifp __unused) { } #define MTAG_GIF 1080679712 static int gif_check_nesting(struct ifnet *ifp, struct mbuf *m) { struct m_tag *mtag; int count; /* * gif may cause infinite recursion calls when misconfigured. * We'll prevent this by detecting loops. * * High nesting level may cause stack exhaustion. * We'll prevent this by introducing upper limit. */ count = 1; mtag = NULL; while ((mtag = m_tag_locate(m, MTAG_GIF, 0, mtag)) != NULL) { if (*(struct ifnet **)(mtag + 1) == ifp) { log(LOG_NOTICE, "%s: loop detected\n", if_name(ifp)); return (EIO); } count++; } if (count > V_max_gif_nesting) { log(LOG_NOTICE, "%s: if_output recursively called too many times(%d)\n", if_name(ifp), count); return (EIO); } mtag = m_tag_alloc(MTAG_GIF, 0, sizeof(struct ifnet *), M_NOWAIT); if (mtag == NULL) return (ENOMEM); *(struct ifnet **)(mtag + 1) = ifp; m_tag_prepend(m, mtag); return (0); } int gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { uint32_t af; if (dst->sa_family == AF_UNSPEC) bcopy(dst->sa_data, &af, sizeof(af)); else af = dst->sa_family; /* * Now save the af in the inbound pkt csum data, this is a cheat since * we are using the inbound csum_data field to carry the af over to * the gif_transmit() routine, avoiding using yet another mtag. */ m->m_pkthdr.csum_data = af; return (ifp->if_transmit(ifp, m)); } void gif_input(struct mbuf *m, struct ifnet *ifp, int proto, uint8_t ecn) { struct etherip_header *eip; #ifdef INET struct ip *ip; #endif #ifdef INET6 struct ip6_hdr *ip6; uint32_t t; #endif struct gif_softc *sc; struct ether_header *eh; struct ifnet *oldifp; - uint32_t gif_options; int isr, n, af; if (ifp == NULL) { /* just in case */ m_freem(m); return; } sc = ifp->if_softc; - gif_options = sc->gif_options; m->m_pkthdr.rcvif = ifp; m_clrprotoflags(m); switch (proto) { #ifdef INET case IPPROTO_IPV4: af = AF_INET; if (m->m_len < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); if (m == NULL) goto drop; ip = mtod(m, struct ip *); if (ip_ecn_egress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED: ECN_NOCARE, &ecn, &ip->ip_tos) == 0) { m_freem(m); goto drop; } break; #endif #ifdef INET6 case IPPROTO_IPV6: af = AF_INET6; if (m->m_len < sizeof(struct ip6_hdr)) m = m_pullup(m, sizeof(struct ip6_hdr)); if (m == NULL) goto drop; t = htonl((uint32_t)ecn << 20); ip6 = mtod(m, struct ip6_hdr *); if (ip6_ecn_egress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED: ECN_NOCARE, &t, &ip6->ip6_flow) == 0) { m_freem(m); goto drop; } break; #endif case IPPROTO_ETHERIP: af = AF_LINK; break; default: m_freem(m); goto drop; } #ifdef MAC mac_ifnet_create_mbuf(ifp, m); #endif if (bpf_peers_present(ifp->if_bpf)) { uint32_t af1 = af; bpf_mtap2(ifp->if_bpf, &af1, sizeof(af1), m); } if ((ifp->if_flags & IFF_MONITOR) != 0) { if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); m_freem(m); return; } if (ng_gif_input_p != NULL) { (*ng_gif_input_p)(ifp, &m, af); if (m == NULL) goto drop; } /* * Put the packet to the network layer input queue according to the * specified address family. * Note: older versions of gif_input directly called network layer * input functions, e.g. ip6_input, here. We changed the policy to * prevent too many recursive calls of such input functions, which * might cause kernel panic. But the change may introduce another * problem; if the input queue is full, packets are discarded. * The kernel stack overflow really happened, and we believed * queue-full rarely occurs, so we changed the policy. */ switch (af) { #ifdef INET case AF_INET: isr = NETISR_IP; break; #endif #ifdef INET6 case AF_INET6: isr = NETISR_IPV6; break; #endif case AF_LINK: n = sizeof(struct etherip_header) + sizeof(struct ether_header); if (n > m->m_len) m = m_pullup(m, n); if (m == NULL) goto drop; eip = mtod(m, struct etherip_header *); if (eip->eip_ver != ETHERIP_VERSION) { /* discard unknown versions */ m_freem(m); goto drop; } m_adj(m, sizeof(struct etherip_header)); m->m_flags &= ~(M_BCAST|M_MCAST); m->m_pkthdr.rcvif = ifp; if (ifp->if_bridge) { oldifp = ifp; eh = mtod(m, struct ether_header *); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { if (ETHER_IS_BROADCAST(eh->ether_dhost)) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); } BRIDGE_INPUT(ifp, m); if (m != NULL && ifp != oldifp) { /* * The bridge gave us back itself or one of the * members for which the frame is addressed. */ ether_demux(ifp, m); return; } } if (m != NULL) m_freem(m); return; default: if (ng_gif_input_orphan_p != NULL) (*ng_gif_input_orphan_p)(ifp, m, af); else m_freem(m); return; } if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); M_SETFIB(m, ifp->if_fib); netisr_dispatch(isr, m); return; drop: if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */ int gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { GIF_RLOCK_TRACKER; struct ifreq *ifr = (struct ifreq*)data; struct sockaddr *dst, *src; struct gif_softc *sc; #ifdef INET struct sockaddr_in *sin = NULL; #endif #ifdef INET6 struct sockaddr_in6 *sin6 = NULL; #endif u_int options; int error; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; case SIOCADDMULTI: case SIOCDELMULTI: case SIOCGIFMTU: case SIOCSIFFLAGS: return (0); case SIOCSIFMTU: if (ifr->ifr_mtu < GIF_MTU_MIN || ifr->ifr_mtu > GIF_MTU_MAX) return (EINVAL); else ifp->if_mtu = ifr->ifr_mtu; return (0); } sx_xlock(&gif_ioctl_sx); sc = ifp->if_softc; if (sc == NULL) { error = ENXIO; goto bad; } error = 0; switch (cmd) { case SIOCSIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif error = EINVAL; switch (cmd) { #ifdef INET case SIOCSIFPHYADDR: src = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_dstaddr); break; #endif #ifdef INET6 case SIOCSIFPHYADDR_IN6: src = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_dstaddr); break; #endif default: goto bad; } /* sa_family must be equal */ if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len) goto bad; /* validate sa_len */ /* check sa_family looks sane for the cmd */ switch (src->sa_family) { #ifdef INET case AF_INET: if (src->sa_len != sizeof(struct sockaddr_in)) goto bad; if (cmd != SIOCSIFPHYADDR) { error = EAFNOSUPPORT; goto bad; } if (satosin(src)->sin_addr.s_addr == INADDR_ANY || satosin(dst)->sin_addr.s_addr == INADDR_ANY) { error = EADDRNOTAVAIL; goto bad; } break; #endif #ifdef INET6 case AF_INET6: if (src->sa_len != sizeof(struct sockaddr_in6)) goto bad; if (cmd != SIOCSIFPHYADDR_IN6) { error = EAFNOSUPPORT; goto bad; } error = EADDRNOTAVAIL; if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr)) goto bad; /* * Check validity of the scope zone ID of the * addresses, and convert it into the kernel * internal form if necessary. */ error = sa6_embedscope(satosin6(src), 0); if (error != 0) goto bad; error = sa6_embedscope(satosin6(dst), 0); if (error != 0) goto bad; break; #endif default: error = EAFNOSUPPORT; goto bad; } error = gif_set_tunnel(ifp, src, dst); break; case SIOCDIFPHYADDR: gif_delete_tunnel(ifp); break; case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: #endif if (sc->gif_family == 0) { error = EADDRNOTAVAIL; break; } GIF_RLOCK(sc); switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: if (sc->gif_family != AF_INET) { error = EADDRNOTAVAIL; break; } sin = (struct sockaddr_in *)&ifr->ifr_addr; memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: if (sc->gif_family != AF_INET6) { error = EADDRNOTAVAIL; break; } sin6 = (struct sockaddr_in6 *) &(((struct in6_ifreq *)data)->ifr_addr); memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); break; #endif default: error = EAFNOSUPPORT; } if (error == 0) { switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: sin->sin_addr = sc->gif_iphdr->ip_src; break; case SIOCGIFPDSTADDR: sin->sin_addr = sc->gif_iphdr->ip_dst; break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: sin6->sin6_addr = sc->gif_ip6hdr->ip6_src; break; case SIOCGIFPDSTADDR_IN6: sin6->sin6_addr = sc->gif_ip6hdr->ip6_dst; break; #endif } } GIF_RUNLOCK(sc); if (error != 0) break; switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: error = prison_if(curthread->td_ucred, (struct sockaddr *)sin); if (error != 0) memset(sin, 0, sizeof(*sin)); break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: error = prison_if(curthread->td_ucred, (struct sockaddr *)sin6); if (error == 0) error = sa6_recoverscope(sin6); if (error != 0) memset(sin6, 0, sizeof(*sin6)); #endif } break; case SIOCGTUNFIB: ifr->ifr_fib = sc->gif_fibnum; break; case SIOCSTUNFIB: if ((error = priv_check(curthread, PRIV_NET_GIF)) != 0) break; if (ifr->ifr_fib >= rt_numfibs) error = EINVAL; else sc->gif_fibnum = ifr->ifr_fib; break; case GIFGOPTS: options = sc->gif_options; error = copyout(&options, ifr->ifr_data, sizeof(options)); break; case GIFSOPTS: if ((error = priv_check(curthread, PRIV_NET_GIF)) != 0) break; error = copyin(ifr->ifr_data, &options, sizeof(options)); if (error) break; if (options & ~GIF_OPTMASK) error = EINVAL; else sc->gif_options = options; break; default: error = EINVAL; break; } bad: sx_xunlock(&gif_ioctl_sx); return (error); } static void gif_detach(struct gif_softc *sc) { sx_assert(&gif_ioctl_sx, SA_XLOCKED); if (sc->gif_ecookie != NULL) encap_detach(sc->gif_ecookie); sc->gif_ecookie = NULL; } static int gif_attach(struct gif_softc *sc, int af) { sx_assert(&gif_ioctl_sx, SA_XLOCKED); switch (af) { #ifdef INET case AF_INET: return (in_gif_attach(sc)); #endif #ifdef INET6 case AF_INET6: return (in6_gif_attach(sc)); #endif } return (EAFNOSUPPORT); } static int gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst) { struct gif_softc *sc = ifp->if_softc; struct gif_softc *tsc; #ifdef INET struct ip *ip; #endif #ifdef INET6 struct ip6_hdr *ip6; #endif void *hdr; int error = 0; if (sc == NULL) return (ENXIO); /* Disallow parallel tunnels unless instructed otherwise. */ if (V_parallel_tunnels == 0) { GIF_LIST_LOCK(); LIST_FOREACH(tsc, &V_gif_softc_list, gif_list) { if (tsc == sc || tsc->gif_family != src->sa_family) continue; #ifdef INET if (tsc->gif_family == AF_INET && tsc->gif_iphdr->ip_src.s_addr == satosin(src)->sin_addr.s_addr && tsc->gif_iphdr->ip_dst.s_addr == satosin(dst)->sin_addr.s_addr) { error = EADDRNOTAVAIL; GIF_LIST_UNLOCK(); goto bad; } #endif #ifdef INET6 if (tsc->gif_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tsc->gif_ip6hdr->ip6_src, &satosin6(src)->sin6_addr) && IN6_ARE_ADDR_EQUAL(&tsc->gif_ip6hdr->ip6_dst, &satosin6(dst)->sin6_addr)) { error = EADDRNOTAVAIL; GIF_LIST_UNLOCK(); goto bad; } #endif } GIF_LIST_UNLOCK(); } switch (src->sa_family) { #ifdef INET case AF_INET: hdr = ip = malloc(sizeof(struct ip), M_GIF, M_WAITOK | M_ZERO); ip->ip_src.s_addr = satosin(src)->sin_addr.s_addr; ip->ip_dst.s_addr = satosin(dst)->sin_addr.s_addr; break; #endif #ifdef INET6 case AF_INET6: hdr = ip6 = malloc(sizeof(struct ip6_hdr), M_GIF, M_WAITOK | M_ZERO); ip6->ip6_src = satosin6(src)->sin6_addr; ip6->ip6_dst = satosin6(dst)->sin6_addr; ip6->ip6_vfc = IPV6_VERSION; break; #endif default: return (EAFNOSUPPORT); }; if (sc->gif_family != src->sa_family) gif_detach(sc); if (sc->gif_family == 0 || sc->gif_family != src->sa_family) error = gif_attach(sc, src->sa_family); GIF_WLOCK(sc); if (sc->gif_family != 0) free(sc->gif_hdr, M_GIF); sc->gif_family = src->sa_family; sc->gif_hdr = hdr; GIF_WUNLOCK(sc); #if defined(INET) || defined(INET6) bad: #endif if (error == 0 && sc->gif_family != 0) { ifp->if_drv_flags |= IFF_DRV_RUNNING; if_link_state_change(ifp, LINK_STATE_UP); } else { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; if_link_state_change(ifp, LINK_STATE_DOWN); } return (error); } static void gif_delete_tunnel(struct ifnet *ifp) { struct gif_softc *sc = ifp->if_softc; int family; if (sc == NULL) return; GIF_WLOCK(sc); family = sc->gif_family; sc->gif_family = 0; GIF_WUNLOCK(sc); if (family != 0) { gif_detach(sc); free(sc->gif_hdr, M_GIF); } ifp->if_drv_flags &= ~IFF_DRV_RUNNING; if_link_state_change(ifp, LINK_STATE_DOWN); } Index: projects/clang380-import/sys/net/if_gre.c =================================================================== --- projects/clang380-import/sys/net/if_gre.c (revision 293015) +++ projects/clang380-import/sys/net/if_gre.c (revision 293016) @@ -1,987 +1,1001 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * Copyright (c) 2014 Andrey V. Elsukov * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Heiko W.Rupp * * IPv6-over-GRE contributed by Gert Doering * * 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 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. * * $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #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 #ifdef INET #include #include #include #include #endif #ifdef INET6 #include #include #include #include #endif #include #include #include #include #include #define GREMTU 1500 static const char grename[] = "gre"; static MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation"); static VNET_DEFINE(struct mtx, gre_mtx); #define V_gre_mtx VNET(gre_mtx) #define GRE_LIST_LOCK_INIT(x) mtx_init(&V_gre_mtx, "gre_mtx", NULL, \ MTX_DEF) #define GRE_LIST_LOCK_DESTROY(x) mtx_destroy(&V_gre_mtx) #define GRE_LIST_LOCK(x) mtx_lock(&V_gre_mtx) #define GRE_LIST_UNLOCK(x) mtx_unlock(&V_gre_mtx) static VNET_DEFINE(LIST_HEAD(, gre_softc), gre_softc_list); #define V_gre_softc_list VNET(gre_softc_list) static struct sx gre_ioctl_sx; SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl"); static int gre_clone_create(struct if_clone *, int, caddr_t); static void gre_clone_destroy(struct ifnet *); static VNET_DEFINE(struct if_clone *, gre_cloner); #define V_gre_cloner VNET(gre_cloner) static void gre_qflush(struct ifnet *); static int gre_transmit(struct ifnet *, struct mbuf *); static int gre_ioctl(struct ifnet *, u_long, caddr_t); static int gre_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); static void gre_updatehdr(struct gre_softc *); static int gre_set_tunnel(struct ifnet *, struct sockaddr *, struct sockaddr *); static void gre_delete_tunnel(struct ifnet *); SYSCTL_DECL(_net_link); static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW, 0, "Generic Routing Encapsulation"); #ifndef MAX_GRE_NEST /* * This macro controls the default upper limitation on nesting of gre tunnels. * Since, setting a large value to this macro with a careless configuration * may introduce system crash, we don't allow any nestings by default. * If you need to configure nested gre tunnels, you can define this macro * in your kernel configuration file. However, if you do so, please be * careful to configure the tunnels so that it won't make a loop. */ #define MAX_GRE_NEST 1 #endif static VNET_DEFINE(int, max_gre_nesting) = MAX_GRE_NEST; #define V_max_gre_nesting VNET(max_gre_nesting) SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels"); static void vnet_gre_init(const void *unused __unused) { LIST_INIT(&V_gre_softc_list); GRE_LIST_LOCK_INIT(); V_gre_cloner = if_clone_simple(grename, gre_clone_create, gre_clone_destroy, 0); } VNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_gre_init, NULL); static void vnet_gre_uninit(const void *unused __unused) { if_clone_detach(V_gre_cloner); GRE_LIST_LOCK_DESTROY(); } VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_gre_uninit, NULL); static int gre_clone_create(struct if_clone *ifc, int unit, caddr_t params) { struct gre_softc *sc; sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO); sc->gre_fibnum = curthread->td_proc->p_fibnum; GRE2IFP(sc) = if_alloc(IFT_TUNNEL); GRE_LOCK_INIT(sc); GRE2IFP(sc)->if_softc = sc; if_initname(GRE2IFP(sc), grename, unit); GRE2IFP(sc)->if_mtu = sc->gre_mtu = GREMTU; GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; GRE2IFP(sc)->if_output = gre_output; GRE2IFP(sc)->if_ioctl = gre_ioctl; GRE2IFP(sc)->if_transmit = gre_transmit; GRE2IFP(sc)->if_qflush = gre_qflush; GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE; GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE; if_attach(GRE2IFP(sc)); bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t)); GRE_LIST_LOCK(); LIST_INSERT_HEAD(&V_gre_softc_list, sc, gre_list); GRE_LIST_UNLOCK(); return (0); } static void gre_clone_destroy(struct ifnet *ifp) { struct gre_softc *sc; sx_xlock(&gre_ioctl_sx); sc = ifp->if_softc; gre_delete_tunnel(ifp); GRE_LIST_LOCK(); LIST_REMOVE(sc, gre_list); GRE_LIST_UNLOCK(); bpfdetach(ifp); if_detach(ifp); ifp->if_softc = NULL; sx_xunlock(&gre_ioctl_sx); if_free(ifp); GRE_LOCK_DESTROY(sc); free(sc, M_GRE); } static int gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { GRE_RLOCK_TRACKER; struct ifreq *ifr = (struct ifreq *)data; struct sockaddr *src, *dst; struct gre_softc *sc; #ifdef INET struct sockaddr_in *sin = NULL; #endif #ifdef INET6 struct sockaddr_in6 *sin6 = NULL; #endif uint32_t opt; int error; switch (cmd) { case SIOCSIFMTU: /* XXX: */ if (ifr->ifr_mtu < 576) return (EINVAL); break; case SIOCSIFADDR: ifp->if_flags |= IFF_UP; case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: return (0); case GRESADDRS: case GRESADDRD: case GREGADDRS: case GREGADDRD: case GRESPROTO: case GREGPROTO: return (EOPNOTSUPP); } src = dst = NULL; sx_xlock(&gre_ioctl_sx); sc = ifp->if_softc; if (sc == NULL) { error = ENXIO; goto end; } error = 0; switch (cmd) { case SIOCSIFMTU: GRE_WLOCK(sc); sc->gre_mtu = ifr->ifr_mtu; gre_updatehdr(sc); GRE_WUNLOCK(sc); goto end; case SIOCSIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif error = EINVAL; switch (cmd) { #ifdef INET case SIOCSIFPHYADDR: src = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_dstaddr); break; #endif #ifdef INET6 case SIOCSIFPHYADDR_IN6: src = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_dstaddr); break; #endif default: error = EAFNOSUPPORT; goto end; } /* sa_family must be equal */ if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len) goto end; /* validate sa_len */ switch (src->sa_family) { #ifdef INET case AF_INET: if (src->sa_len != sizeof(struct sockaddr_in)) goto end; break; #endif #ifdef INET6 case AF_INET6: if (src->sa_len != sizeof(struct sockaddr_in6)) goto end; break; #endif default: error = EAFNOSUPPORT; goto end; } /* check sa_family looks sane for the cmd */ error = EAFNOSUPPORT; switch (cmd) { #ifdef INET case SIOCSIFPHYADDR: if (src->sa_family == AF_INET) break; goto end; #endif #ifdef INET6 case SIOCSIFPHYADDR_IN6: if (src->sa_family == AF_INET6) break; goto end; #endif } error = EADDRNOTAVAIL; switch (src->sa_family) { #ifdef INET case AF_INET: if (satosin(src)->sin_addr.s_addr == INADDR_ANY || satosin(dst)->sin_addr.s_addr == INADDR_ANY) goto end; break; #endif #ifdef INET6 case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr) || IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr)) goto end; /* * Check validity of the scope zone ID of the * addresses, and convert it into the kernel * internal form if necessary. */ error = sa6_embedscope(satosin6(src), 0); if (error != 0) goto end; error = sa6_embedscope(satosin6(dst), 0); if (error != 0) goto end; #endif }; error = gre_set_tunnel(ifp, src, dst); break; case SIOCDIFPHYADDR: gre_delete_tunnel(ifp); break; case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: #endif if (sc->gre_family == 0) { error = EADDRNOTAVAIL; break; } GRE_RLOCK(sc); switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: if (sc->gre_family != AF_INET) { error = EADDRNOTAVAIL; break; } sin = (struct sockaddr_in *)&ifr->ifr_addr; memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: if (sc->gre_family != AF_INET6) { error = EADDRNOTAVAIL; break; } sin6 = (struct sockaddr_in6 *) &(((struct in6_ifreq *)data)->ifr_addr); memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); break; #endif } if (error == 0) { switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: sin->sin_addr = sc->gre_oip.ip_src; break; case SIOCGIFPDSTADDR: sin->sin_addr = sc->gre_oip.ip_dst; break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: sin6->sin6_addr = sc->gre_oip6.ip6_src; break; case SIOCGIFPDSTADDR_IN6: sin6->sin6_addr = sc->gre_oip6.ip6_dst; break; #endif } } GRE_RUNLOCK(sc); if (error != 0) break; switch (cmd) { #ifdef INET case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: error = prison_if(curthread->td_ucred, (struct sockaddr *)sin); if (error != 0) memset(sin, 0, sizeof(*sin)); break; #endif #ifdef INET6 case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: error = prison_if(curthread->td_ucred, (struct sockaddr *)sin6); if (error == 0) error = sa6_recoverscope(sin6); if (error != 0) memset(sin6, 0, sizeof(*sin6)); #endif } break; case SIOCGTUNFIB: ifr->ifr_fib = sc->gre_fibnum; break; case SIOCSTUNFIB: if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) break; if (ifr->ifr_fib >= rt_numfibs) error = EINVAL; else sc->gre_fibnum = ifr->ifr_fib; break; case GRESKEY: if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) break; if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) break; if (sc->gre_key != opt) { GRE_WLOCK(sc); sc->gre_key = opt; gre_updatehdr(sc); GRE_WUNLOCK(sc); } break; case GREGKEY: error = copyout(&sc->gre_key, ifr->ifr_data, sizeof(sc->gre_key)); break; case GRESOPTS: if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0) break; if ((error = copyin(ifr->ifr_data, &opt, sizeof(opt))) != 0) break; if (opt & ~GRE_OPTMASK) error = EINVAL; else { if (sc->gre_options != opt) { GRE_WLOCK(sc); sc->gre_options = opt; gre_updatehdr(sc); GRE_WUNLOCK(sc); } } break; case GREGOPTS: error = copyout(&sc->gre_options, ifr->ifr_data, sizeof(sc->gre_options)); break; default: error = EINVAL; break; } end: sx_xunlock(&gre_ioctl_sx); return (error); } static void gre_updatehdr(struct gre_softc *sc) { struct grehdr *gh = NULL; uint32_t *opts; uint16_t flags; GRE_WLOCK_ASSERT(sc); switch (sc->gre_family) { #ifdef INET case AF_INET: sc->gre_hlen = sizeof(struct greip); sc->gre_oip.ip_v = IPPROTO_IPV4; sc->gre_oip.ip_hl = sizeof(struct ip) >> 2; sc->gre_oip.ip_p = IPPROTO_GRE; gh = &sc->gre_gihdr->gi_gre; break; #endif #ifdef INET6 case AF_INET6: sc->gre_hlen = sizeof(struct greip6); sc->gre_oip6.ip6_vfc = IPV6_VERSION; sc->gre_oip6.ip6_nxt = IPPROTO_GRE; gh = &sc->gre_gi6hdr->gi6_gre; break; #endif default: return; } flags = 0; opts = gh->gre_opts; if (sc->gre_options & GRE_ENABLE_CSUM) { flags |= GRE_FLAGS_CP; sc->gre_hlen += 2 * sizeof(uint16_t); *opts++ = 0; } if (sc->gre_key != 0) { flags |= GRE_FLAGS_KP; sc->gre_hlen += sizeof(uint32_t); *opts++ = htonl(sc->gre_key); } if (sc->gre_options & GRE_ENABLE_SEQ) { flags |= GRE_FLAGS_SP; sc->gre_hlen += sizeof(uint32_t); *opts++ = 0; } else sc->gre_oseq = 0; gh->gre_flags = htons(flags); GRE2IFP(sc)->if_mtu = sc->gre_mtu - sc->gre_hlen; } static void gre_detach(struct gre_softc *sc) { sx_assert(&gre_ioctl_sx, SA_XLOCKED); if (sc->gre_ecookie != NULL) encap_detach(sc->gre_ecookie); sc->gre_ecookie = NULL; } static int gre_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst) { struct gre_softc *sc, *tsc; #ifdef INET6 struct ip6_hdr *ip6; #endif #ifdef INET struct ip *ip; #endif void *hdr; int error; sx_assert(&gre_ioctl_sx, SA_XLOCKED); GRE_LIST_LOCK(); sc = ifp->if_softc; LIST_FOREACH(tsc, &V_gre_softc_list, gre_list) { if (tsc == sc || tsc->gre_family != src->sa_family) continue; #ifdef INET if (tsc->gre_family == AF_INET && tsc->gre_oip.ip_src.s_addr == satosin(src)->sin_addr.s_addr && tsc->gre_oip.ip_dst.s_addr == satosin(dst)->sin_addr.s_addr) { GRE_LIST_UNLOCK(); return (EADDRNOTAVAIL); } #endif #ifdef INET6 if (tsc->gre_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_src, &satosin6(src)->sin6_addr) && IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_dst, &satosin6(dst)->sin6_addr)) { GRE_LIST_UNLOCK(); return (EADDRNOTAVAIL); } #endif } GRE_LIST_UNLOCK(); switch (src->sa_family) { #ifdef INET case AF_INET: hdr = ip = malloc(sizeof(struct greip) + 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); ip->ip_src = satosin(src)->sin_addr; ip->ip_dst = satosin(dst)->sin_addr; break; #endif #ifdef INET6 case AF_INET6: hdr = ip6 = malloc(sizeof(struct greip6) + 3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO); ip6->ip6_src = satosin6(src)->sin6_addr; ip6->ip6_dst = satosin6(dst)->sin6_addr; break; #endif default: return (EAFNOSUPPORT); } if (sc->gre_family != 0) gre_detach(sc); GRE_WLOCK(sc); if (sc->gre_family != 0) free(sc->gre_hdr, M_GRE); sc->gre_family = src->sa_family; sc->gre_hdr = hdr; sc->gre_oseq = 0; sc->gre_iseq = UINT32_MAX; gre_updatehdr(sc); GRE_WUNLOCK(sc); error = 0; switch (src->sa_family) { #ifdef INET case AF_INET: error = in_gre_attach(sc); break; #endif #ifdef INET6 case AF_INET6: error = in6_gre_attach(sc); break; #endif } if (error == 0) { ifp->if_drv_flags |= IFF_DRV_RUNNING; if_link_state_change(ifp, LINK_STATE_UP); } return (error); } static void gre_delete_tunnel(struct ifnet *ifp) { struct gre_softc *sc = ifp->if_softc; int family; GRE_WLOCK(sc); family = sc->gre_family; sc->gre_family = 0; GRE_WUNLOCK(sc); if (family != 0) { gre_detach(sc); free(sc->gre_hdr, M_GRE); } ifp->if_drv_flags &= ~IFF_DRV_RUNNING; if_link_state_change(ifp, LINK_STATE_DOWN); } int gre_input(struct mbuf **mp, int *offp, int proto) { struct gre_softc *sc; struct grehdr *gh; struct ifnet *ifp; struct mbuf *m; - uint32_t *opts, key; + uint32_t *opts; +#ifdef notyet + uint32_t key; +#endif uint16_t flags; int hlen, isr, af; m = *mp; sc = encap_getarg(m); KASSERT(sc != NULL, ("encap_getarg returned NULL")); ifp = GRE2IFP(sc); hlen = *offp + sizeof(struct grehdr) + 4 * sizeof(uint32_t); if (m->m_pkthdr.len < hlen) goto drop; if (m->m_len < hlen) { m = m_pullup(m, hlen); if (m == NULL) goto drop; } gh = (struct grehdr *)mtodo(m, *offp); flags = ntohs(gh->gre_flags); if (flags & ~GRE_FLAGS_MASK) goto drop; opts = gh->gre_opts; hlen = 2 * sizeof(uint16_t); if (flags & GRE_FLAGS_CP) { /* reserved1 field must be zero */ if (((uint16_t *)opts)[1] != 0) goto drop; if (in_cksum_skip(m, m->m_pkthdr.len, *offp) != 0) goto drop; hlen += 2 * sizeof(uint16_t); opts++; } if (flags & GRE_FLAGS_KP) { +#ifdef notyet + /* + * XXX: The current implementation uses the key only for outgoing + * packets. But we can check the key value here, or even in the + * encapcheck function. + */ key = ntohl(*opts); +#endif hlen += sizeof(uint32_t); opts++; + } +#ifdef notyet } else key = 0; - /* + if (sc->gre_key != 0 && (key != sc->gre_key || key != 0)) goto drop; - */ +#endif if (flags & GRE_FLAGS_SP) { - /* seq = ntohl(*opts); */ +#ifdef notyet + seq = ntohl(*opts); +#endif hlen += sizeof(uint32_t); } switch (ntohs(gh->gre_proto)) { case ETHERTYPE_WCCP: /* * For WCCP skip an additional 4 bytes if after GRE header * doesn't follow an IP header. */ if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40) hlen += sizeof(uint32_t); /* FALLTHROUGH */ case ETHERTYPE_IP: isr = NETISR_IP; af = AF_INET; break; case ETHERTYPE_IPV6: isr = NETISR_IPV6; af = AF_INET6; break; default: goto drop; } m_adj(m, *offp + hlen); m_clrprotoflags(m); m->m_pkthdr.rcvif = ifp; M_SETFIB(m, ifp->if_fib); #ifdef MAC mac_ifnet_create_mbuf(ifp, m); #endif BPF_MTAP2(ifp, &af, sizeof(af), m); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); if ((ifp->if_flags & IFF_MONITOR) != 0) m_freem(m); else netisr_dispatch(isr, m); return (IPPROTO_DONE); drop: if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); return (IPPROTO_DONE); } #define MTAG_GRE 1307983903 static int gre_check_nesting(struct ifnet *ifp, struct mbuf *m) { struct m_tag *mtag; int count; count = 1; mtag = NULL; while ((mtag = m_tag_locate(m, MTAG_GRE, 0, mtag)) != NULL) { if (*(struct ifnet **)(mtag + 1) == ifp) { log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname); return (EIO); } count++; } if (count > V_max_gre_nesting) { log(LOG_NOTICE, "%s: if_output recursively called too many times(%d)\n", ifp->if_xname, count); return (EIO); } mtag = m_tag_alloc(MTAG_GRE, 0, sizeof(struct ifnet *), M_NOWAIT); if (mtag == NULL) return (ENOMEM); *(struct ifnet **)(mtag + 1) = ifp; m_tag_prepend(m, mtag); return (0); } static int gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { uint32_t af; int error; #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error != 0) goto drop; #endif if ((ifp->if_flags & IFF_MONITOR) != 0 || (ifp->if_flags & IFF_UP) == 0) { error = ENETDOWN; goto drop; } error = gre_check_nesting(ifp, m); if (error != 0) goto drop; m->m_flags &= ~(M_BCAST|M_MCAST); if (dst->sa_family == AF_UNSPEC) bcopy(dst->sa_data, &af, sizeof(af)); else af = dst->sa_family; BPF_MTAP2(ifp, &af, sizeof(af), m); m->m_pkthdr.csum_data = af; /* save af for if_transmit */ return (ifp->if_transmit(ifp, m)); drop: m_freem(m); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (error); } static void gre_setseqn(struct grehdr *gh, uint32_t seq) { uint32_t *opts; uint16_t flags; opts = gh->gre_opts; flags = ntohs(gh->gre_flags); KASSERT((flags & GRE_FLAGS_SP) != 0, ("gre_setseqn called, but GRE_FLAGS_SP isn't set ")); if (flags & GRE_FLAGS_CP) opts++; if (flags & GRE_FLAGS_KP) opts++; *opts = htonl(seq); } static int gre_transmit(struct ifnet *ifp, struct mbuf *m) { GRE_RLOCK_TRACKER; struct gre_softc *sc; struct grehdr *gh; uint32_t iaf, oaf, oseq; int error, hlen, olen, plen; int want_seq, want_csum; plen = 0; sc = ifp->if_softc; if (sc == NULL) { error = ENETDOWN; m_freem(m); goto drop; } GRE_RLOCK(sc); if (sc->gre_family == 0) { GRE_RUNLOCK(sc); error = ENETDOWN; m_freem(m); goto drop; } iaf = m->m_pkthdr.csum_data; oaf = sc->gre_family; hlen = sc->gre_hlen; want_seq = (sc->gre_options & GRE_ENABLE_SEQ) != 0; if (want_seq) oseq = sc->gre_oseq++; /* XXX */ else oseq = 0; /* Make compiler happy. */ want_csum = (sc->gre_options & GRE_ENABLE_CSUM) != 0; M_SETFIB(m, sc->gre_fibnum); M_PREPEND(m, hlen, M_NOWAIT); if (m == NULL) { GRE_RUNLOCK(sc); error = ENOBUFS; goto drop; } bcopy(sc->gre_hdr, mtod(m, void *), hlen); GRE_RUNLOCK(sc); switch (oaf) { #ifdef INET case AF_INET: olen = sizeof(struct ip); break; #endif #ifdef INET6 case AF_INET6: olen = sizeof(struct ip6_hdr); break; #endif default: error = ENETDOWN; goto drop; } gh = (struct grehdr *)mtodo(m, olen); switch (iaf) { #ifdef INET case AF_INET: gh->gre_proto = htons(ETHERTYPE_IP); break; #endif #ifdef INET6 case AF_INET6: gh->gre_proto = htons(ETHERTYPE_IPV6); break; #endif default: error = ENETDOWN; goto drop; } if (want_seq) gre_setseqn(gh, oseq); if (want_csum) { *(uint16_t *)gh->gre_opts = in_cksum_skip(m, m->m_pkthdr.len, olen); } plen = m->m_pkthdr.len - hlen; switch (oaf) { #ifdef INET case AF_INET: error = in_gre_output(m, iaf, hlen); break; #endif #ifdef INET6 case AF_INET6: error = in6_gre_output(m, iaf, hlen); break; #endif default: m_freem(m); error = ENETDOWN; }; drop: if (error) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); else { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OBYTES, plen); } return (error); } static void gre_qflush(struct ifnet *ifp __unused) { } static int gremodevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: case MOD_UNLOAD: break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t gre_mod = { "if_gre", gremodevent, 0 }; DECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(if_gre, 1); Index: projects/clang380-import/sys/net/if_llatbl.c =================================================================== --- projects/clang380-import/sys/net/if_llatbl.c (revision 293015) +++ projects/clang380-import/sys/net/if_llatbl.c (revision 293016) @@ -1,872 +1,958 @@ /* * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved. * Copyright (c) 2004-2008 Qing Li. All rights reserved. * Copyright (c) 2008 Kip Macy. 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 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 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$"); #include "opt_ddb.h" #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables"); static VNET_DEFINE(SLIST_HEAD(, lltable), lltables) = SLIST_HEAD_INITIALIZER(lltables); #define V_lltables VNET(lltables) struct rwlock lltable_rwlock; RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock"); static void lltable_unlink(struct lltable *llt); static void llentries_unlink(struct lltable *llt, struct llentries *head); static void htable_unlink_entry(struct llentry *lle); static void htable_link_entry(struct lltable *llt, struct llentry *lle); static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg); /* * Dump lle state for a specific address family. */ static int lltable_dump_af(struct lltable *llt, struct sysctl_req *wr) { int error; LLTABLE_LOCK_ASSERT(); if (llt->llt_ifp->if_flags & IFF_LOOPBACK) return (0); error = 0; IF_AFDATA_RLOCK(llt->llt_ifp); error = lltable_foreach_lle(llt, (llt_foreach_cb_t *)llt->llt_dump_entry, wr); IF_AFDATA_RUNLOCK(llt->llt_ifp); return (error); } /* * Dump arp state for a specific address family. */ int lltable_sysctl_dumparp(int af, struct sysctl_req *wr) { struct lltable *llt; int error = 0; LLTABLE_RLOCK(); SLIST_FOREACH(llt, &V_lltables, llt_link) { if (llt->llt_af == af) { error = lltable_dump_af(llt, wr); if (error != 0) goto done; } } done: LLTABLE_RUNLOCK(); return (error); } /* * Common function helpers for chained hash table. */ /* * Runs specified callback for each entry in @llt. * Caller does the locking. * */ static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg) { struct llentry *lle, *next; int i, error; error = 0; for (i = 0; i < llt->llt_hsize; i++) { LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { error = f(llt, lle, farg); if (error != 0) break; } } return (error); } static void htable_link_entry(struct lltable *llt, struct llentry *lle) { struct llentries *lleh; uint32_t hashidx; if ((lle->la_flags & LLE_LINKED) != 0) return; IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp); hashidx = llt->llt_hash(lle, llt->llt_hsize); lleh = &llt->lle_head[hashidx]; lle->lle_tbl = llt; lle->lle_head = lleh; lle->la_flags |= LLE_LINKED; LIST_INSERT_HEAD(lleh, lle, lle_next); } static void htable_unlink_entry(struct llentry *lle) { if ((lle->la_flags & LLE_LINKED) != 0) { IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp); LIST_REMOVE(lle, lle_next); lle->la_flags &= ~(LLE_VALID | LLE_LINKED); #if 0 lle->lle_tbl = NULL; lle->lle_head = NULL; #endif } } struct prefix_match_data { const struct sockaddr *addr; const struct sockaddr *mask; struct llentries dchain; u_int flags; }; static int htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg) { struct prefix_match_data *pmd; pmd = (struct prefix_match_data *)farg; if (llt->llt_match_prefix(pmd->addr, pmd->mask, pmd->flags, lle)) { LLE_WLOCK(lle); LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain); } return (0); } static void htable_prefix_free(struct lltable *llt, const struct sockaddr *addr, const struct sockaddr *mask, u_int flags) { struct llentry *lle, *next; struct prefix_match_data pmd; bzero(&pmd, sizeof(pmd)); pmd.addr = addr; pmd.mask = mask; pmd.flags = flags; LIST_INIT(&pmd.dchain); IF_AFDATA_WLOCK(llt->llt_ifp); /* Push matching lles to chain */ lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd); llentries_unlink(llt, &pmd.dchain); IF_AFDATA_WUNLOCK(llt->llt_ifp); LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next) lltable_free_entry(llt, lle); } static void htable_free_tbl(struct lltable *llt) { free(llt->lle_head, M_LLTABLE); free(llt, M_LLTABLE); } static void llentries_unlink(struct lltable *llt, struct llentries *head) { struct llentry *lle, *next; LIST_FOREACH_SAFE(lle, head, lle_chain, next) llt->llt_unlink_entry(lle); } /* * Helper function used to drop all mbufs in hold queue. * * Returns the number of held packets, if any, that were dropped. */ size_t lltable_drop_entry_queue(struct llentry *lle) { size_t pkts_dropped; struct mbuf *next; LLE_WLOCK_ASSERT(lle); pkts_dropped = 0; while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) { next = lle->la_hold->m_nextpkt; m_freem(lle->la_hold); lle->la_hold = next; lle->la_numheld--; pkts_dropped++; } KASSERT(lle->la_numheld == 0, ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, lle->la_numheld, pkts_dropped)); return (pkts_dropped); } void lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr) + const char *linkhdr, size_t linkhdrsize, int lladdr_off) { - bcopy(lladdr, &lle->ll_addr, ifp->if_addrlen); + memcpy(lle->r_linkdata, linkhdr, linkhdrsize); + lle->r_hdrlen = linkhdrsize; + lle->ll_addr = &lle->r_linkdata[lladdr_off]; lle->la_flags |= LLE_VALID; lle->r_flags |= RLLE_VALID; } /* * Tries to update @lle link-level address. * Since update requires AFDATA WLOCK, function * drops @lle lock, acquires AFDATA lock and then acquires * @lle lock to maintain lock order. * * Returns 1 on success. */ int lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr) + const char *linkhdr, size_t linkhdrsize, int lladdr_off) { /* Perform real LLE update */ /* use afdata WLOCK to update fields */ LLE_WLOCK_ASSERT(lle); LLE_ADDREF(lle); LLE_WUNLOCK(lle); IF_AFDATA_WLOCK(ifp); LLE_WLOCK(lle); /* * Since we droppped LLE lock, other thread might have deleted * this lle. Check and return */ if ((lle->la_flags & LLE_DELETED) != 0) { IF_AFDATA_WUNLOCK(ifp); LLE_FREE_LOCKED(lle); return (0); } /* Update data */ - lltable_set_entry_addr(ifp, lle, lladdr); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off); IF_AFDATA_WUNLOCK(ifp); LLE_REMREF(lle); return (1); } + /* + * Helper function used to pre-compute full/partial link-layer + * header data suitable for feeding into if_output(). + */ +int +lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr, + char *buf, size_t *bufsize, int *lladdr_off) +{ + struct if_encap_req ereq; + int error; + + bzero(buf, *bufsize); + bzero(&ereq, sizeof(ereq)); + ereq.buf = buf; + ereq.bufsize = *bufsize; + ereq.rtype = IFENCAP_LL; + ereq.family = family; + ereq.lladdr = lladdr; + ereq.lladdr_len = ifp->if_addrlen; + error = ifp->if_requestencap(ifp, &ereq); + if (error == 0) { + *bufsize = ereq.bufsize; + *lladdr_off = ereq.lladdr_off; + } + + return (error); +} + /* + * Update link-layer header for given @lle after + * interface lladdr was changed. + */ +static int +llentry_update_ifaddr(struct lltable *llt, struct llentry *lle, void *farg) +{ + struct ifnet *ifp; + u_char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + u_char *lladdr; + int lladdr_off; + + ifp = (struct ifnet *)farg; + + lladdr = lle->ll_addr; + + LLE_WLOCK(lle); + if ((lle->la_flags & LLE_VALID) == 0) { + LLE_WUNLOCK(lle); + return (0); + } + + if ((lle->la_flags & LLE_IFADDR) != 0) + lladdr = IF_LLADDR(ifp); + + linkhdrsize = sizeof(linkhdr); + lltable_calc_llheader(ifp, llt->llt_af, lladdr, linkhdr, &linkhdrsize, + &lladdr_off); + memcpy(lle->r_linkdata, linkhdr, linkhdrsize); + LLE_WUNLOCK(lle); + + return (0); +} + +/* + * Update all calculated headers for given @llt + */ +void +lltable_update_ifaddr(struct lltable *llt) +{ + + if (llt->llt_ifp->if_flags & IFF_LOOPBACK) + return; + + IF_AFDATA_WLOCK(llt->llt_ifp); + lltable_foreach_lle(llt, llentry_update_ifaddr, llt->llt_ifp); + IF_AFDATA_WUNLOCK(llt->llt_ifp); +} + +/* * * Performes generic cleanup routines and frees lle. * * Called for non-linked entries, with callouts and * other AF-specific cleanups performed. * * @lle must be passed WLOCK'ed * * Returns the number of held packets, if any, that were dropped. */ size_t llentry_free(struct llentry *lle) { size_t pkts_dropped; LLE_WLOCK_ASSERT(lle); KASSERT((lle->la_flags & LLE_LINKED) == 0, ("freeing linked lle")); pkts_dropped = lltable_drop_entry_queue(lle); LLE_FREE_LOCKED(lle); return (pkts_dropped); } /* * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp). * * If found the llentry * is returned referenced and unlocked. */ struct llentry * llentry_alloc(struct ifnet *ifp, struct lltable *lt, struct sockaddr_storage *dst) { struct llentry *la, *la_tmp; IF_AFDATA_RLOCK(ifp); la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst); IF_AFDATA_RUNLOCK(ifp); if (la != NULL) { LLE_ADDREF(la); LLE_WUNLOCK(la); return (la); } if ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { la = lltable_alloc_entry(lt, 0, (struct sockaddr *)dst); if (la == NULL) return (NULL); IF_AFDATA_WLOCK(ifp); LLE_WLOCK(la); /* Prefer any existing LLE over newly-created one */ la_tmp = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst); if (la_tmp == NULL) lltable_link_entry(lt, la); IF_AFDATA_WUNLOCK(ifp); if (la_tmp != NULL) { lltable_free_entry(lt, la); la = la_tmp; } LLE_ADDREF(la); LLE_WUNLOCK(la); } return (la); } /* * Free all entries from given table and free itself. */ static int lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg) { struct llentries *dchain; dchain = (struct llentries *)farg; LLE_WLOCK(lle); LIST_INSERT_HEAD(dchain, lle, lle_chain); return (0); } /* * Free all entries from given table and free itself. */ void lltable_free(struct lltable *llt) { struct llentry *lle, *next; struct llentries dchain; KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); lltable_unlink(llt); LIST_INIT(&dchain); IF_AFDATA_WLOCK(llt->llt_ifp); /* Push all lles to @dchain */ lltable_foreach_lle(llt, lltable_free_cb, &dchain); llentries_unlink(llt, &dchain); IF_AFDATA_WUNLOCK(llt->llt_ifp); LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) { if (callout_stop(&lle->lle_timer) > 0) LLE_REMREF(lle); llentry_free(lle); } llt->llt_free_tbl(llt); } #if 0 void lltable_drain(int af) { struct lltable *llt; struct llentry *lle; register int i; LLTABLE_RLOCK(); SLIST_FOREACH(llt, &V_lltables, llt_link) { if (llt->llt_af != af) continue; for (i=0; i < llt->llt_hsize; i++) { LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { LLE_WLOCK(lle); if (lle->la_hold) { m_freem(lle->la_hold); lle->la_hold = NULL; } LLE_WUNLOCK(lle); } } } LLTABLE_RUNLOCK(); } #endif /* * Deletes an address from given lltable. * Used for userland interaction to remove * individual entries. Skips entries added by OS. */ int lltable_delete_addr(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { struct llentry *lle; struct ifnet *ifp; ifp = llt->llt_ifp; IF_AFDATA_WLOCK(ifp); lle = lla_lookup(llt, LLE_EXCLUSIVE, l3addr); if (lle == NULL) { IF_AFDATA_WUNLOCK(ifp); return (ENOENT); } if ((lle->la_flags & LLE_IFADDR) != 0 && (flags & LLE_IFADDR) == 0) { IF_AFDATA_WUNLOCK(ifp); LLE_WUNLOCK(lle); return (EPERM); } lltable_unlink_entry(llt, lle); IF_AFDATA_WUNLOCK(ifp); llt->llt_delete_entry(llt, lle); return (0); } void lltable_prefix_free(int af, struct sockaddr *addr, struct sockaddr *mask, u_int flags) { struct lltable *llt; LLTABLE_RLOCK(); SLIST_FOREACH(llt, &V_lltables, llt_link) { if (llt->llt_af != af) continue; llt->llt_prefix_free(llt, addr, mask, flags); } LLTABLE_RUNLOCK(); } struct lltable * lltable_allocate_htbl(uint32_t hsize) { struct lltable *llt; int i; llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK | M_ZERO); llt->llt_hsize = hsize; llt->lle_head = malloc(sizeof(struct llentries) * hsize, M_LLTABLE, M_WAITOK | M_ZERO); for (i = 0; i < llt->llt_hsize; i++) LIST_INIT(&llt->lle_head[i]); /* Set some default callbacks */ llt->llt_link_entry = htable_link_entry; llt->llt_unlink_entry = htable_unlink_entry; llt->llt_prefix_free = htable_prefix_free; llt->llt_foreach_entry = htable_foreach_lle; llt->llt_free_tbl = htable_free_tbl; return (llt); } /* * Links lltable to global llt list. */ void lltable_link(struct lltable *llt) { LLTABLE_WLOCK(); SLIST_INSERT_HEAD(&V_lltables, llt, llt_link); LLTABLE_WUNLOCK(); } static void lltable_unlink(struct lltable *llt) { LLTABLE_WLOCK(); SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); LLTABLE_WUNLOCK(); } /* * External methods used by lltable consumers */ int lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg) { return (llt->llt_foreach_entry(llt, f, farg)); } struct llentry * lltable_alloc_entry(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { return (llt->llt_alloc_entry(llt, flags, l3addr)); } void lltable_free_entry(struct lltable *llt, struct llentry *lle) { llt->llt_free_entry(llt, lle); } void lltable_link_entry(struct lltable *llt, struct llentry *lle) { llt->llt_link_entry(llt, lle); } void lltable_unlink_entry(struct lltable *llt, struct llentry *lle) { llt->llt_unlink_entry(lle); } void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa) { struct lltable *llt; llt = lle->lle_tbl; llt->llt_fill_sa_entry(lle, sa); } struct ifnet * lltable_get_ifp(const struct lltable *llt) { return (llt->llt_ifp); } int lltable_get_af(const struct lltable *llt) { return (llt->llt_af); } /* * Called in route_output when rtm_flags contains RTF_LLDATA. */ int lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) { struct sockaddr_dl *dl = (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; struct ifnet *ifp; struct lltable *llt; struct llentry *lle, *lle_tmp; + uint8_t linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; u_int laflags = 0; int error; KASSERT(dl != NULL && dl->sdl_family == AF_LINK, ("%s: invalid dl\n", __func__)); ifp = ifnet_byindex(dl->sdl_index); if (ifp == NULL) { log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", __func__, dl->sdl_index); return EINVAL; } /* XXX linked list may be too expensive */ LLTABLE_RLOCK(); SLIST_FOREACH(llt, &V_lltables, llt_link) { if (llt->llt_af == dst->sa_family && llt->llt_ifp == ifp) break; } LLTABLE_RUNLOCK(); KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); error = 0; switch (rtm->rtm_type) { case RTM_ADD: /* Add static LLE */ laflags = 0; if (rtm->rtm_rmx.rmx_expire == 0) laflags = LLE_STATIC; lle = lltable_alloc_entry(llt, laflags, dst); if (lle == NULL) return (ENOMEM); - bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, dst->sa_family, LLADDR(dl), + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return (EINVAL); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, + lladdr_off); if ((rtm->rtm_flags & RTF_ANNOUNCE)) lle->la_flags |= LLE_PUB; - lle->la_flags |= LLE_VALID; - lle->r_flags |= RLLE_VALID; lle->la_expire = rtm->rtm_rmx.rmx_expire; laflags = lle->la_flags; /* Try to link new entry */ lle_tmp = NULL; IF_AFDATA_WLOCK(ifp); LLE_WLOCK(lle); lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, dst); if (lle_tmp != NULL) { /* Check if we are trying to replace immutable entry */ if ((lle_tmp->la_flags & LLE_IFADDR) != 0) { IF_AFDATA_WUNLOCK(ifp); LLE_WUNLOCK(lle_tmp); lltable_free_entry(llt, lle); return (EPERM); } /* Unlink existing entry from table */ lltable_unlink_entry(llt, lle_tmp); } lltable_link_entry(llt, lle); IF_AFDATA_WUNLOCK(ifp); if (lle_tmp != NULL) { EVENTHANDLER_INVOKE(lle_event, lle_tmp,LLENTRY_EXPIRED); lltable_free_entry(llt, lle_tmp); } /* * By invoking LLE handler here we might get * two events on static LLE entry insertion * in routing socket. However, since we might have * other subscribers we need to generate this event. */ EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED); LLE_WUNLOCK(lle); #ifdef INET /* gratuitous ARP */ if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) arprequest(ifp, &((struct sockaddr_in *)dst)->sin_addr, &((struct sockaddr_in *)dst)->sin_addr, (u_char *)LLADDR(dl)); #endif break; case RTM_DELETE: return (lltable_delete_addr(llt, 0, dst)); default: error = EINVAL; } return (error); } #ifdef DDB struct llentry_sa { struct llentry base; struct sockaddr l3_addr; }; static void llatbl_lle_show(struct llentry_sa *la) { struct llentry *lle; uint8_t octet[6]; lle = &la->base; db_printf("lle=%p\n", lle); db_printf(" lle_next=%p\n", lle->lle_next.le_next); db_printf(" lle_lock=%p\n", &lle->lle_lock); db_printf(" lle_tbl=%p\n", lle->lle_tbl); db_printf(" lle_head=%p\n", lle->lle_head); db_printf(" la_hold=%p\n", lle->la_hold); db_printf(" la_numheld=%d\n", lle->la_numheld); db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire); db_printf(" la_flags=0x%04x\n", lle->la_flags); db_printf(" la_asked=%u\n", lle->la_asked); db_printf(" la_preempt=%u\n", lle->la_preempt); db_printf(" ln_state=%d\n", lle->ln_state); db_printf(" ln_router=%u\n", lle->ln_router); db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick); db_printf(" lle_refcnt=%d\n", lle->lle_refcnt); - bcopy(&lle->ll_addr.mac16, octet, sizeof(octet)); + bcopy(lle->ll_addr, octet, sizeof(octet)); db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n", octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]); db_printf(" lle_timer=%p\n", &lle->lle_timer); switch (la->l3_addr.sa_family) { #ifdef INET case AF_INET: { struct sockaddr_in *sin; char l3s[INET_ADDRSTRLEN]; sin = (struct sockaddr_in *)&la->l3_addr; inet_ntoa_r(sin->sin_addr, l3s); db_printf(" l3_addr=%s\n", l3s); break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6; char l3s[INET6_ADDRSTRLEN]; sin6 = (struct sockaddr_in6 *)&la->l3_addr; ip6_sprintf(l3s, &sin6->sin6_addr); db_printf(" l3_addr=%s\n", l3s); break; } #endif default: db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family); break; } } DB_SHOW_COMMAND(llentry, db_show_llentry) { if (!have_addr) { db_printf("usage: show llentry \n"); return; } llatbl_lle_show((struct llentry_sa *)addr); } static void llatbl_llt_show(struct lltable *llt) { int i; struct llentry *lle; db_printf("llt=%p llt_af=%d llt_ifp=%p\n", llt, llt->llt_af, llt->llt_ifp); for (i = 0; i < llt->llt_hsize; i++) { LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { llatbl_lle_show((struct llentry_sa *)lle); if (db_pager_quit) return; } } } DB_SHOW_COMMAND(lltable, db_show_lltable) { if (!have_addr) { db_printf("usage: show lltable \n"); return; } llatbl_llt_show((struct lltable *)addr); } DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables) { VNET_ITERATOR_DECL(vnet_iter); struct lltable *llt; VNET_FOREACH(vnet_iter) { CURVNET_SET_QUIET(vnet_iter); #ifdef VIMAGE db_printf("vnet=%p\n", curvnet); #endif SLIST_FOREACH(llt, &V_lltables, llt_link) { db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n", llt, llt->llt_af, llt->llt_ifp, (llt->llt_ifp != NULL) ? llt->llt_ifp->if_xname : "?"); if (have_addr && addr != 0) /* verbose */ llatbl_llt_show(llt); if (db_pager_quit) { CURVNET_RESTORE(); return; } } CURVNET_RESTORE(); } } #endif Index: projects/clang380-import/sys/net/if_llatbl.h =================================================================== --- projects/clang380-import/sys/net/if_llatbl.h (revision 293015) +++ projects/clang380-import/sys/net/if_llatbl.h (revision 293016) @@ -1,264 +1,268 @@ /* * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved. * Copyright (c) 2004-2008 Qing Li. All rights reserved. * Copyright (c) 2008 Kip Macy. 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 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 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$"); #ifndef _NET_IF_LLATBL_H_ #define _NET_IF_LLATBL_H_ #include #include struct ifnet; struct sysctl_req; struct rt_msghdr; struct rt_addrinfo; struct llentry; LIST_HEAD(llentries, llentry); extern struct rwlock lltable_rwlock; #define LLTABLE_RLOCK() rw_rlock(&lltable_rwlock) #define LLTABLE_RUNLOCK() rw_runlock(&lltable_rwlock) #define LLTABLE_WLOCK() rw_wlock(&lltable_rwlock) #define LLTABLE_WUNLOCK() rw_wunlock(&lltable_rwlock) #define LLTABLE_LOCK_ASSERT() rw_assert(&lltable_rwlock, RA_LOCKED) +#define LLE_MAX_LINKHDR 24 /* Full IB header */ /* * Code referencing llentry must at least hold * a shared lock */ struct llentry { LIST_ENTRY(llentry) lle_next; union { struct in_addr addr4; struct in6_addr addr6; } r_l3addr; - union { - uint64_t mac_aligned; - uint16_t mac16[3]; - uint8_t mac8[20]; /* IB needs 20 bytes. */ - } ll_addr; + char r_linkdata[LLE_MAX_LINKHDR]; /* L2 data */ + uint8_t r_hdrlen; /* length for LL header */ + uint8_t spare0[3]; uint16_t r_flags; /* LLE runtime flags */ uint16_t r_skip_req; /* feedback from fast path */ - uint64_t spare1; struct lltable *lle_tbl; struct llentries *lle_head; void (*lle_free)(struct llentry *); struct mbuf *la_hold; int la_numheld; /* # of packets currently held */ time_t la_expire; uint16_t la_flags; uint16_t la_asked; uint16_t la_preempt; int16_t ln_state; /* IPv6 has ND6_LLINFO_NOSTATE == -2 */ uint16_t ln_router; time_t ln_ntick; time_t lle_remtime; /* Real time remaining */ time_t lle_hittime; /* Time when r_skip_req was unset */ int lle_refcnt; + char *ll_addr; /* link-layer address */ LIST_ENTRY(llentry) lle_chain; /* chain of deleted items */ struct callout lle_timer; struct rwlock lle_lock; struct mtx req_mtx; }; #define LLE_WLOCK(lle) rw_wlock(&(lle)->lle_lock) #define LLE_RLOCK(lle) rw_rlock(&(lle)->lle_lock) #define LLE_WUNLOCK(lle) rw_wunlock(&(lle)->lle_lock) #define LLE_RUNLOCK(lle) rw_runlock(&(lle)->lle_lock) #define LLE_DOWNGRADE(lle) rw_downgrade(&(lle)->lle_lock) #define LLE_TRY_UPGRADE(lle) rw_try_upgrade(&(lle)->lle_lock) #define LLE_LOCK_INIT(lle) rw_init_flags(&(lle)->lle_lock, "lle", RW_DUPOK) #define LLE_LOCK_DESTROY(lle) rw_destroy(&(lle)->lle_lock) #define LLE_WLOCK_ASSERT(lle) rw_assert(&(lle)->lle_lock, RA_WLOCKED) #define LLE_REQ_INIT(lle) mtx_init(&(lle)->req_mtx, "lle req", \ NULL, MTX_DEF) #define LLE_REQ_DESTROY(lle) mtx_destroy(&(lle)->req_mtx) #define LLE_REQ_LOCK(lle) mtx_lock(&(lle)->req_mtx) #define LLE_REQ_UNLOCK(lle) mtx_unlock(&(lle)->req_mtx) #define LLE_IS_VALID(lle) (((lle) != NULL) && ((lle) != (void *)-1)) #define LLE_ADDREF(lle) do { \ LLE_WLOCK_ASSERT(lle); \ KASSERT((lle)->lle_refcnt >= 0, \ ("negative refcnt %d on lle %p", \ (lle)->lle_refcnt, (lle))); \ (lle)->lle_refcnt++; \ } while (0) #define LLE_REMREF(lle) do { \ LLE_WLOCK_ASSERT(lle); \ KASSERT((lle)->lle_refcnt > 0, \ ("bogus refcnt %d on lle %p", \ (lle)->lle_refcnt, (lle))); \ (lle)->lle_refcnt--; \ } while (0) #define LLE_FREE_LOCKED(lle) do { \ if ((lle)->lle_refcnt == 1) \ (lle)->lle_free(lle); \ else { \ LLE_REMREF(lle); \ LLE_WUNLOCK(lle); \ } \ /* guard against invalid refs */ \ (lle) = NULL; \ } while (0) #define LLE_FREE(lle) do { \ LLE_WLOCK(lle); \ LLE_FREE_LOCKED(lle); \ } while (0) typedef struct llentry *(llt_lookup_t)(struct lltable *, u_int flags, const struct sockaddr *l3addr); typedef struct llentry *(llt_alloc_t)(struct lltable *, u_int flags, const struct sockaddr *l3addr); typedef void (llt_delete_t)(struct lltable *, struct llentry *); typedef void (llt_prefix_free_t)(struct lltable *, const struct sockaddr *addr, const struct sockaddr *mask, u_int flags); typedef int (llt_dump_entry_t)(struct lltable *, struct llentry *, struct sysctl_req *); typedef uint32_t (llt_hash_t)(const struct llentry *, uint32_t); typedef int (llt_match_prefix_t)(const struct sockaddr *, const struct sockaddr *, u_int, struct llentry *); typedef void (llt_free_entry_t)(struct lltable *, struct llentry *); typedef void (llt_fill_sa_entry_t)(const struct llentry *, struct sockaddr *); typedef void (llt_free_tbl_t)(struct lltable *); typedef void (llt_link_entry_t)(struct lltable *, struct llentry *); typedef void (llt_unlink_entry_t)(struct llentry *); typedef int (llt_foreach_cb_t)(struct lltable *, struct llentry *, void *); typedef int (llt_foreach_entry_t)(struct lltable *, llt_foreach_cb_t *, void *); struct lltable { SLIST_ENTRY(lltable) llt_link; int llt_af; int llt_hsize; struct llentries *lle_head; struct ifnet *llt_ifp; llt_lookup_t *llt_lookup; llt_alloc_t *llt_alloc_entry; llt_delete_t *llt_delete_entry; llt_prefix_free_t *llt_prefix_free; llt_dump_entry_t *llt_dump_entry; llt_hash_t *llt_hash; llt_match_prefix_t *llt_match_prefix; llt_free_entry_t *llt_free_entry; llt_foreach_entry_t *llt_foreach_entry; llt_link_entry_t *llt_link_entry; llt_unlink_entry_t *llt_unlink_entry; llt_fill_sa_entry_t *llt_fill_sa_entry; llt_free_tbl_t *llt_free_tbl; }; MALLOC_DECLARE(M_LLTABLE); /* * LLentry flags */ #define LLE_DELETED 0x0001 /* entry must be deleted */ #define LLE_STATIC 0x0002 /* entry is static */ #define LLE_IFADDR 0x0004 /* entry is interface addr */ #define LLE_VALID 0x0008 /* ll_addr is valid */ #define LLE_REDIRECT 0x0010 /* installed by redirect; has host rtentry */ #define LLE_PUB 0x0020 /* publish entry ??? */ #define LLE_LINKED 0x0040 /* linked to lookup structure */ /* LLE request flags */ #define LLE_EXCLUSIVE 0x2000 /* return lle xlocked */ #define LLE_UNLOCKED 0x4000 /* return lle unlocked */ +#define LLE_ADDRONLY 0x4000 /* return lladdr instead of full header */ +#define LLE_CREATE 0x8000 /* hint to avoid lle lookup */ /* LLE flags used by fastpath code */ #define RLLE_VALID 0x0001 /* entry is valid */ #define RLLE_IFADDR LLE_IFADDR /* entry is ifaddr */ #define LLATBL_HASH(key, mask) \ (((((((key >> 8) ^ key) >> 8) ^ key) >> 8) ^ key) & mask) struct lltable *lltable_allocate_htbl(uint32_t hsize); void lltable_free(struct lltable *); void lltable_link(struct lltable *llt); void lltable_prefix_free(int, struct sockaddr *, struct sockaddr *, u_int); #if 0 void lltable_drain(int); #endif int lltable_sysctl_dumparp(int, struct sysctl_req *); size_t llentry_free(struct llentry *); struct llentry *llentry_alloc(struct ifnet *, struct lltable *, struct sockaddr_storage *); /* helper functions */ size_t lltable_drop_entry_queue(struct llentry *); void lltable_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr); + const char *linkhdr, size_t linkhdrsize, int lladdr_off); int lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, - const char *lladdr); + const char *linkhdr, size_t linkhdrsize, int lladdr_off); +int lltable_calc_llheader(struct ifnet *ifp, int family, char *lladdr, + char *buf, size_t *bufsize, int *lladdr_off); +void lltable_update_ifaddr(struct lltable *llt); struct llentry *lltable_alloc_entry(struct lltable *llt, u_int flags, const struct sockaddr *l4addr); void lltable_free_entry(struct lltable *llt, struct llentry *lle); int lltable_delete_addr(struct lltable *llt, u_int flags, const struct sockaddr *l3addr); void lltable_link_entry(struct lltable *llt, struct llentry *lle); void lltable_unlink_entry(struct lltable *llt, struct llentry *lle); void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa); struct ifnet *lltable_get_ifp(const struct lltable *llt); int lltable_get_af(const struct lltable *llt); int lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg); /* * Generic link layer address lookup function. */ static __inline struct llentry * lla_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { return (llt->llt_lookup(llt, flags, l3addr)); } int lla_rt_output(struct rt_msghdr *, struct rt_addrinfo *); #include enum { LLENTRY_RESOLVED, LLENTRY_TIMEDOUT, LLENTRY_DELETED, LLENTRY_EXPIRED, }; typedef void (*lle_event_fn)(void *, struct llentry *, int); EVENTHANDLER_DECLARE(lle_event, lle_event_fn); #endif /* _NET_IF_LLATBL_H_ */ Index: projects/clang380-import/sys/net/if_var.h =================================================================== --- projects/clang380-import/sys/net/if_var.h (revision 293015) +++ projects/clang380-import/sys/net/if_var.h (revision 293016) @@ -1,620 +1,664 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: @(#)if.h 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #ifndef _NET_IF_VAR_H_ #define _NET_IF_VAR_H_ /* * Structures defining a network interface, providing a packet * transport mechanism (ala level 0 of the PUP protocols). * * Each interface accepts output datagrams of a specified maximum * length, and provides higher level routines with input datagrams * received from its medium. * * Output occurs when the routine if_output is called, with three parameters: * (*ifp->if_output)(ifp, m, dst, rt) * Here m is the mbuf chain to be sent and dst is the destination address. * The output routine encapsulates the supplied datagram if necessary, * and then transmits it on its medium. * * On input, each interface unwraps the data received by it, and either * places it on the input queue of an internetwork datagram routine * and posts the associated software interrupt, or passes the datagram to a raw * packet input routine. * * Routines exist for locating interfaces by their addresses * or for locating an interface on a certain network, as well as more general * routing and gateway routines maintaining information used to locate * interfaces. These routines live in the files if.c and route.c */ struct rtentry; /* ifa_rtrequest */ struct rt_addrinfo; /* ifa_rtrequest */ struct socket; struct carp_if; struct carp_softc; struct ifvlantrunk; struct route; /* if_output */ struct vnet; struct ifmedia; struct netmap_adapter; #ifdef _KERNEL #include /* ifqueue only? */ #include #include #endif /* _KERNEL */ #include #include /* XXX */ #include /* struct ifqueue */ #include /* XXX */ #include /* XXX */ #include /* if_link_task */ #define IF_DUNIT_NONE -1 #include TAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ TAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ TAILQ_HEAD(ifmultihead, ifmultiaddr); TAILQ_HEAD(ifgrouphead, ifg_group); #ifdef _KERNEL VNET_DECLARE(struct pfil_head, link_pfil_hook); /* packet filter hooks */ #define V_link_pfil_hook VNET(link_pfil_hook) #define HHOOK_IPSEC_INET 0 #define HHOOK_IPSEC_INET6 1 #define HHOOK_IPSEC_COUNT 2 VNET_DECLARE(struct hhook_head *, ipsec_hhh_in[HHOOK_IPSEC_COUNT]); VNET_DECLARE(struct hhook_head *, ipsec_hhh_out[HHOOK_IPSEC_COUNT]); #define V_ipsec_hhh_in VNET(ipsec_hhh_in) #define V_ipsec_hhh_out VNET(ipsec_hhh_out) #endif /* _KERNEL */ typedef enum { IFCOUNTER_IPACKETS = 0, IFCOUNTER_IERRORS, IFCOUNTER_OPACKETS, IFCOUNTER_OERRORS, IFCOUNTER_COLLISIONS, IFCOUNTER_IBYTES, IFCOUNTER_OBYTES, IFCOUNTER_IMCASTS, IFCOUNTER_OMCASTS, IFCOUNTER_IQDROPS, IFCOUNTER_OQDROPS, IFCOUNTER_NOPROTO, IFCOUNTERS /* Array size. */ } ift_counter; typedef struct ifnet * if_t; typedef void (*if_start_fn_t)(if_t); typedef int (*if_ioctl_fn_t)(if_t, u_long, caddr_t); typedef void (*if_init_fn_t)(void *); typedef void (*if_qflush_fn_t)(if_t); typedef int (*if_transmit_fn_t)(if_t, struct mbuf *); typedef uint64_t (*if_get_counter_t)(if_t, ift_counter); struct ifnet_hw_tsomax { u_int tsomaxbytes; /* TSO total burst length limit in bytes */ u_int tsomaxsegcount; /* TSO maximum segment count */ u_int tsomaxsegsize; /* TSO maximum segment size in bytes */ }; +/* Interface encap request types */ +typedef enum { + IFENCAP_LL = 1 /* pre-calculate link-layer header */ +} ife_type; + /* + * The structure below allows to request various pre-calculated L2/L3 headers + * for different media. Requests varies by type (rtype field). + * + * IFENCAP_LL type: pre-calculates link header based on address family + * and destination lladdr. + * + * Input data fields: + * buf: pointer to destination buffer + * bufsize: buffer size + * flags: IFENCAP_FLAG_BROADCAST if destination is broadcast + * family: address family defined by AF_ constant. + * lladdr: pointer to link-layer address + * lladdr_len: length of link-layer address + * hdata: pointer to L3 header (optional, used for ARP requests). + * Output data fields: + * buf: encap data is stored here + * bufsize: resulting encap length is stored here + * lladdr_off: offset of link-layer address from encap hdr start + * hdata: L3 header may be altered if necessary + */ + +struct if_encap_req { + u_char *buf; /* Destination buffer (w) */ + size_t bufsize; /* size of provided buffer (r) */ + ife_type rtype; /* request type (r) */ + uint32_t flags; /* Request flags (r) */ + int family; /* Address family AF_* (r) */ + int lladdr_off; /* offset from header start (w) */ + int lladdr_len; /* lladdr length (r) */ + char *lladdr; /* link-level address pointer (r) */ + char *hdata; /* Upper layer header data (rw) */ +}; + +#define IFENCAP_FLAG_BROADCAST 0x02 /* Destination is broadcast */ + + +/* * Structure defining a network interface. * * Size ILP32: 592 (approx) * LP64: 1048 (approx) */ struct ifnet { /* General book keeping of interface lists. */ TAILQ_ENTRY(ifnet) if_link; /* all struct ifnets are chained */ LIST_ENTRY(ifnet) if_clones; /* interfaces of a cloner */ TAILQ_HEAD(, ifg_list) if_groups; /* linked list of groups per if */ /* protected by if_addr_lock */ u_char if_alloctype; /* if_type at time of allocation */ /* Driver and protocol specific information that remains stable. */ void *if_softc; /* pointer to driver state */ void *if_llsoftc; /* link layer softc */ void *if_l2com; /* pointer to protocol bits */ const char *if_dname; /* driver name */ int if_dunit; /* unit or IF_DUNIT_NONE */ u_short if_index; /* numeric abbreviation for this if */ short if_index_reserved; /* spare space to grow if_index */ char if_xname[IFNAMSIZ]; /* external name (name + unit) */ char *if_description; /* interface description */ /* Variable fields that are touched by the stack and drivers. */ int if_flags; /* up/down, broadcast, etc. */ int if_drv_flags; /* driver-managed status flags */ int if_capabilities; /* interface features & capabilities */ int if_capenable; /* enabled features & capabilities */ void *if_linkmib; /* link-type-specific MIB data */ size_t if_linkmiblen; /* length of above data */ u_int if_refcount; /* reference count */ /* These fields are shared with struct if_data. */ uint8_t if_type; /* ethernet, tokenring, etc */ uint8_t if_addrlen; /* media address length */ uint8_t if_hdrlen; /* media header length */ uint8_t if_link_state; /* current link state */ uint32_t if_mtu; /* maximum transmission unit */ uint32_t if_metric; /* routing metric (external only) */ uint64_t if_baudrate; /* linespeed */ uint64_t if_hwassist; /* HW offload capabilities, see IFCAP */ time_t if_epoch; /* uptime at attach or stat reset */ struct timeval if_lastchange; /* time of last administrative change */ struct ifaltq if_snd; /* output queue (includes altq) */ struct task if_linktask; /* task for link change events */ /* Addresses of different protocol families assigned to this if. */ struct rwlock if_addr_lock; /* lock to protect address lists */ /* * if_addrhead is the list of all addresses associated to * an interface. * Some code in the kernel assumes that first element * of the list has type AF_LINK, and contains sockaddr_dl * addresses which store the link-level address and the name * of the interface. * However, access to the AF_LINK address through this * field is deprecated. Use if_addr or ifaddr_byindex() instead. */ struct ifaddrhead if_addrhead; /* linked list of addresses per if */ struct ifmultihead if_multiaddrs; /* multicast addresses configured */ int if_amcount; /* number of all-multicast requests */ struct ifaddr *if_addr; /* pointer to link-level address */ const u_int8_t *if_broadcastaddr; /* linklevel broadcast bytestring */ struct rwlock if_afdata_lock; void *if_afdata[AF_MAX]; int if_afdata_initialized; /* Additional features hung off the interface. */ u_int if_fib; /* interface FIB */ struct vnet *if_vnet; /* pointer to network stack instance */ struct vnet *if_home_vnet; /* where this ifnet originates from */ struct ifvlantrunk *if_vlantrunk; /* pointer to 802.1q data */ struct bpf_if *if_bpf; /* packet filter structure */ int if_pcount; /* number of promiscuous listeners */ void *if_bridge; /* bridge glue */ void *if_lagg; /* lagg glue */ void *if_pf_kif; /* pf glue */ struct carp_if *if_carp; /* carp interface structure */ struct label *if_label; /* interface MAC label */ struct netmap_adapter *if_netmap; /* netmap(4) softc */ /* Various procedures of the layer2 encapsulation and drivers. */ int (*if_output) /* output routine (enqueue) */ (struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); void (*if_input) /* input routine (from h/w driver) */ (struct ifnet *, struct mbuf *); if_start_fn_t if_start; /* initiate output routine */ if_ioctl_fn_t if_ioctl; /* ioctl routine */ if_init_fn_t if_init; /* Init routine */ int (*if_resolvemulti) /* validate/resolve multicast */ (struct ifnet *, struct sockaddr **, struct sockaddr *); if_qflush_fn_t if_qflush; /* flush any queue */ if_transmit_fn_t if_transmit; /* initiate output routine */ void (*if_reassign) /* reassign to vnet routine */ (struct ifnet *, struct vnet *, char *); if_get_counter_t if_get_counter; /* get counter values */ + int (*if_requestencap) /* make link header from request */ + (struct ifnet *, struct if_encap_req *); /* Statistics. */ counter_u64_t if_counters[IFCOUNTERS]; /* Stuff that's only temporary and doesn't belong here. */ /* * Network adapter TSO limits: * =========================== * * If the "if_hw_tsomax" field is zero the maximum segment * length limit does not apply. If the "if_hw_tsomaxsegcount" * or the "if_hw_tsomaxsegsize" field is zero the TSO segment * count limit does not apply. If all three fields are zero, * there is no TSO limit. * * NOTE: The TSO limits should reflect the values used in the * BUSDMA tag a network adapter is using to load a mbuf chain * for transmission. The TCP/IP network stack will subtract * space for all linklevel and protocol level headers and * ensure that the full mbuf chain passed to the network * adapter fits within the given limits. */ u_int if_hw_tsomax; /* TSO maximum size in bytes */ u_int if_hw_tsomaxsegcount; /* TSO maximum segment count */ u_int if_hw_tsomaxsegsize; /* TSO maximum segment size in bytes */ /* * Spare fields to be added before branching a stable branch, so * that structure can be enhanced without changing the kernel * binary interface. */ }; /* for compatibility with other BSDs */ #define if_addrlist if_addrhead #define if_list if_link #define if_name(ifp) ((ifp)->if_xname) /* * Locks for address lists on the network interface. */ #define IF_ADDR_LOCK_INIT(if) rw_init(&(if)->if_addr_lock, "if_addr_lock") #define IF_ADDR_LOCK_DESTROY(if) rw_destroy(&(if)->if_addr_lock) #define IF_ADDR_WLOCK(if) rw_wlock(&(if)->if_addr_lock) #define IF_ADDR_WUNLOCK(if) rw_wunlock(&(if)->if_addr_lock) #define IF_ADDR_RLOCK(if) rw_rlock(&(if)->if_addr_lock) #define IF_ADDR_RUNLOCK(if) rw_runlock(&(if)->if_addr_lock) #define IF_ADDR_LOCK_ASSERT(if) rw_assert(&(if)->if_addr_lock, RA_LOCKED) #define IF_ADDR_WLOCK_ASSERT(if) rw_assert(&(if)->if_addr_lock, RA_WLOCKED) /* * Function variations on locking macros intended to be used by loadable * kernel modules in order to divorce them from the internals of address list * locking. */ void if_addr_rlock(struct ifnet *ifp); /* if_addrhead */ void if_addr_runlock(struct ifnet *ifp); /* if_addrhead */ void if_maddr_rlock(if_t ifp); /* if_multiaddrs */ void if_maddr_runlock(if_t ifp); /* if_multiaddrs */ #ifdef _KERNEL #ifdef _SYS_EVENTHANDLER_H_ /* interface link layer address change event */ typedef void (*iflladdr_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(iflladdr_event, iflladdr_event_handler_t); /* interface address change event */ typedef void (*ifaddr_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifaddr_event, ifaddr_event_handler_t); /* new interface arrival event */ typedef void (*ifnet_arrival_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifnet_arrival_event, ifnet_arrival_event_handler_t); /* interface departure event */ typedef void (*ifnet_departure_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifnet_departure_event, ifnet_departure_event_handler_t); /* Interface link state change event */ typedef void (*ifnet_link_event_handler_t)(void *, struct ifnet *, int); EVENTHANDLER_DECLARE(ifnet_link_event, ifnet_link_event_handler_t); #endif /* _SYS_EVENTHANDLER_H_ */ /* * interface groups */ struct ifg_group { char ifg_group[IFNAMSIZ]; u_int ifg_refcnt; void *ifg_pf_kif; TAILQ_HEAD(, ifg_member) ifg_members; TAILQ_ENTRY(ifg_group) ifg_next; }; struct ifg_member { TAILQ_ENTRY(ifg_member) ifgm_next; struct ifnet *ifgm_ifp; }; struct ifg_list { struct ifg_group *ifgl_group; TAILQ_ENTRY(ifg_list) ifgl_next; }; #ifdef _SYS_EVENTHANDLER_H_ /* group attach event */ typedef void (*group_attach_event_handler_t)(void *, struct ifg_group *); EVENTHANDLER_DECLARE(group_attach_event, group_attach_event_handler_t); /* group detach event */ typedef void (*group_detach_event_handler_t)(void *, struct ifg_group *); EVENTHANDLER_DECLARE(group_detach_event, group_detach_event_handler_t); /* group change event */ typedef void (*group_change_event_handler_t)(void *, const char *); EVENTHANDLER_DECLARE(group_change_event, group_change_event_handler_t); #endif /* _SYS_EVENTHANDLER_H_ */ #define IF_AFDATA_LOCK_INIT(ifp) \ rw_init(&(ifp)->if_afdata_lock, "if_afdata") #define IF_AFDATA_WLOCK(ifp) rw_wlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_RLOCK(ifp) rw_rlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_WUNLOCK(ifp) rw_wunlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_RUNLOCK(ifp) rw_runlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_LOCK(ifp) IF_AFDATA_WLOCK(ifp) #define IF_AFDATA_UNLOCK(ifp) IF_AFDATA_WUNLOCK(ifp) #define IF_AFDATA_TRYLOCK(ifp) rw_try_wlock(&(ifp)->if_afdata_lock) #define IF_AFDATA_DESTROY(ifp) rw_destroy(&(ifp)->if_afdata_lock) #define IF_AFDATA_LOCK_ASSERT(ifp) rw_assert(&(ifp)->if_afdata_lock, RA_LOCKED) #define IF_AFDATA_RLOCK_ASSERT(ifp) rw_assert(&(ifp)->if_afdata_lock, RA_RLOCKED) #define IF_AFDATA_WLOCK_ASSERT(ifp) rw_assert(&(ifp)->if_afdata_lock, RA_WLOCKED) #define IF_AFDATA_UNLOCK_ASSERT(ifp) rw_assert(&(ifp)->if_afdata_lock, RA_UNLOCKED) /* * 72 was chosen below because it is the size of a TCP/IP * header (40) + the minimum mss (32). */ #define IF_MINMTU 72 #define IF_MAXMTU 65535 #define TOEDEV(ifp) ((ifp)->if_llsoftc) /* * The ifaddr structure contains information about one address * of an interface. They are maintained by the different address families, * are allocated and attached when an address is set, and are linked * together so all addresses for an interface can be located. * * NOTE: a 'struct ifaddr' is always at the beginning of a larger * chunk of malloc'ed memory, where we store the three addresses * (ifa_addr, ifa_dstaddr and ifa_netmask) referenced here. */ struct ifaddr { struct sockaddr *ifa_addr; /* address of interface */ struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */ #define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ struct sockaddr *ifa_netmask; /* used to determine subnet */ struct ifnet *ifa_ifp; /* back-pointer to interface */ struct carp_softc *ifa_carp; /* pointer to CARP data */ TAILQ_ENTRY(ifaddr) ifa_link; /* queue macro glue */ void (*ifa_rtrequest) /* check or clean routes (+ or -)'d */ (int, struct rtentry *, struct rt_addrinfo *); u_short ifa_flags; /* mostly rt_flags for cloning */ #define IFA_ROUTE RTF_UP /* route installed */ #define IFA_RTSELF RTF_HOST /* loopback route to self installed */ u_int ifa_refcnt; /* references to this structure */ counter_u64_t ifa_ipackets; counter_u64_t ifa_opackets; counter_u64_t ifa_ibytes; counter_u64_t ifa_obytes; }; /* For compatibility with other BSDs. SCTP uses it. */ #define ifa_list ifa_link struct ifaddr * ifa_alloc(size_t size, int flags); void ifa_free(struct ifaddr *ifa); void ifa_ref(struct ifaddr *ifa); /* * Multicast address structure. This is analogous to the ifaddr * structure except that it keeps track of multicast addresses. */ struct ifmultiaddr { TAILQ_ENTRY(ifmultiaddr) ifma_link; /* queue macro glue */ struct sockaddr *ifma_addr; /* address this membership is for */ struct sockaddr *ifma_lladdr; /* link-layer translation, if any */ struct ifnet *ifma_ifp; /* back-pointer to interface */ u_int ifma_refcount; /* reference count */ void *ifma_protospec; /* protocol-specific state, if any */ struct ifmultiaddr *ifma_llifma; /* pointer to ifma for ifma_lladdr */ }; extern struct rwlock ifnet_rwlock; extern struct sx ifnet_sxlock; #define IFNET_WLOCK() do { \ sx_xlock(&ifnet_sxlock); \ rw_wlock(&ifnet_rwlock); \ } while (0) #define IFNET_WUNLOCK() do { \ rw_wunlock(&ifnet_rwlock); \ sx_xunlock(&ifnet_sxlock); \ } while (0) /* * To assert the ifnet lock, you must know not only whether it's for read or * write, but also whether it was acquired with sleep support or not. */ #define IFNET_RLOCK_ASSERT() sx_assert(&ifnet_sxlock, SA_SLOCKED) #define IFNET_RLOCK_NOSLEEP_ASSERT() rw_assert(&ifnet_rwlock, RA_RLOCKED) #define IFNET_WLOCK_ASSERT() do { \ sx_assert(&ifnet_sxlock, SA_XLOCKED); \ rw_assert(&ifnet_rwlock, RA_WLOCKED); \ } while (0) #define IFNET_RLOCK() sx_slock(&ifnet_sxlock) #define IFNET_RLOCK_NOSLEEP() rw_rlock(&ifnet_rwlock) #define IFNET_RUNLOCK() sx_sunlock(&ifnet_sxlock) #define IFNET_RUNLOCK_NOSLEEP() rw_runlock(&ifnet_rwlock) /* * Look up an ifnet given its index; the _ref variant also acquires a * reference that must be freed using if_rele(). It is almost always a bug * to call ifnet_byindex() instead if ifnet_byindex_ref(). */ struct ifnet *ifnet_byindex(u_short idx); struct ifnet *ifnet_byindex_locked(u_short idx); struct ifnet *ifnet_byindex_ref(u_short idx); /* * Given the index, ifaddr_byindex() returns the one and only * link-level ifaddr for the interface. You are not supposed to use * it to traverse the list of addresses associated to the interface. */ struct ifaddr *ifaddr_byindex(u_short idx); VNET_DECLARE(struct ifnethead, ifnet); VNET_DECLARE(struct ifgrouphead, ifg_head); VNET_DECLARE(int, if_index); VNET_DECLARE(struct ifnet *, loif); /* first loopback interface */ #define V_ifnet VNET(ifnet) #define V_ifg_head VNET(ifg_head) #define V_if_index VNET(if_index) #define V_loif VNET(loif) int if_addgroup(struct ifnet *, const char *); int if_delgroup(struct ifnet *, const char *); int if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **); int if_allmulti(struct ifnet *, int); struct ifnet* if_alloc(u_char); void if_attach(struct ifnet *); void if_dead(struct ifnet *); int if_delmulti(struct ifnet *, struct sockaddr *); void if_delmulti_ifma(struct ifmultiaddr *); void if_detach(struct ifnet *); void if_vmove(struct ifnet *, struct vnet *); void if_purgeaddrs(struct ifnet *); void if_delallmulti(struct ifnet *); void if_down(struct ifnet *); struct ifmultiaddr * if_findmulti(struct ifnet *, const struct sockaddr *); void if_free(struct ifnet *); void if_initname(struct ifnet *, const char *, int); void if_link_state_change(struct ifnet *, int); int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3); void if_ref(struct ifnet *); void if_rele(struct ifnet *); int if_setlladdr(struct ifnet *, const u_char *, int); void if_up(struct ifnet *); int ifioctl(struct socket *, u_long, caddr_t, struct thread *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); struct ifnet *ifunit_ref(const char *); int ifa_add_loopback_route(struct ifaddr *, struct sockaddr *); int ifa_del_loopback_route(struct ifaddr *, struct sockaddr *); int ifa_switch_loopback_route(struct ifaddr *, struct sockaddr *); struct ifaddr *ifa_ifwithaddr(const struct sockaddr *); int ifa_ifwithaddr_check(const struct sockaddr *); struct ifaddr *ifa_ifwithbroadaddr(const struct sockaddr *, int); struct ifaddr *ifa_ifwithdstaddr(const struct sockaddr *, int); struct ifaddr *ifa_ifwithnet(const struct sockaddr *, int, int); struct ifaddr *ifa_ifwithroute(int, const struct sockaddr *, struct sockaddr *, u_int); struct ifaddr *ifaof_ifpforaddr(const struct sockaddr *, struct ifnet *); int ifa_preferred(struct ifaddr *, struct ifaddr *); int if_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen); typedef void *if_com_alloc_t(u_char type, struct ifnet *ifp); typedef void if_com_free_t(void *com, u_char type); void if_register_com_alloc(u_char type, if_com_alloc_t *a, if_com_free_t *f); void if_deregister_com_alloc(u_char type); void if_data_copy(struct ifnet *, struct if_data *); uint64_t if_get_counter_default(struct ifnet *, ift_counter); void if_inc_counter(struct ifnet *, ift_counter, int64_t); #define IF_LLADDR(ifp) \ LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) uint64_t if_setbaudrate(if_t ifp, uint64_t baudrate); uint64_t if_getbaudrate(if_t ifp); int if_setcapabilities(if_t ifp, int capabilities); int if_setcapabilitiesbit(if_t ifp, int setbit, int clearbit); int if_getcapabilities(if_t ifp); int if_togglecapenable(if_t ifp, int togglecap); int if_setcapenable(if_t ifp, int capenable); int if_setcapenablebit(if_t ifp, int setcap, int clearcap); int if_getcapenable(if_t ifp); const char *if_getdname(if_t ifp); int if_setdev(if_t ifp, void *dev); int if_setdrvflagbits(if_t ifp, int if_setflags, int clear_flags); int if_getdrvflags(if_t ifp); int if_setdrvflags(if_t ifp, int flags); int if_clearhwassist(if_t ifp); int if_sethwassistbits(if_t ifp, int toset, int toclear); int if_sethwassist(if_t ifp, int hwassist_bit); int if_gethwassist(if_t ifp); int if_setsoftc(if_t ifp, void *softc); void *if_getsoftc(if_t ifp); int if_setflags(if_t ifp, int flags); int if_setmtu(if_t ifp, int mtu); int if_getmtu(if_t ifp); int if_getmtu_family(if_t ifp, int family); int if_setflagbits(if_t ifp, int set, int clear); int if_getflags(if_t ifp); int if_sendq_empty(if_t ifp); int if_setsendqready(if_t ifp); int if_setsendqlen(if_t ifp, int tx_desc_count); int if_input(if_t ifp, struct mbuf* sendmp); int if_sendq_prepend(if_t ifp, struct mbuf *m); struct mbuf *if_dequeue(if_t ifp); int if_setifheaderlen(if_t ifp, int len); void if_setrcvif(struct mbuf *m, if_t ifp); void if_setvtag(struct mbuf *m, u_int16_t tag); u_int16_t if_getvtag(struct mbuf *m); int if_vlantrunkinuse(if_t ifp); caddr_t if_getlladdr(if_t ifp); void *if_gethandle(u_char); void if_bpfmtap(if_t ifp, struct mbuf *m); void if_etherbpfmtap(if_t ifp, struct mbuf *m); void if_vlancap(if_t ifp); int if_setupmultiaddr(if_t ifp, void *mta, int *cnt, int max); int if_multiaddr_array(if_t ifp, void *mta, int *cnt, int max); int if_multiaddr_count(if_t ifp, int max); int if_getamcount(if_t ifp); struct ifaddr * if_getifaddr(if_t ifp); /* Functions */ void if_setinitfn(if_t ifp, void (*)(void *)); void if_setioctlfn(if_t ifp, int (*)(if_t, u_long, caddr_t)); void if_setstartfn(if_t ifp, void (*)(if_t)); void if_settransmitfn(if_t ifp, if_transmit_fn_t); void if_setqflushfn(if_t ifp, if_qflush_fn_t); void if_setgetcounterfn(if_t ifp, if_get_counter_t); /* Revisit the below. These are inline functions originally */ int drbr_inuse_drv(if_t ifp, struct buf_ring *br); struct mbuf* drbr_dequeue_drv(if_t ifp, struct buf_ring *br); int drbr_needs_enqueue_drv(if_t ifp, struct buf_ring *br); int drbr_enqueue_drv(if_t ifp, struct buf_ring *br, struct mbuf *m); /* TSO */ void if_hw_tsomax_common(if_t ifp, struct ifnet_hw_tsomax *); int if_hw_tsomax_update(if_t ifp, struct ifnet_hw_tsomax *); #ifdef DEVICE_POLLING enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; typedef int poll_handler_t(if_t ifp, enum poll_cmd cmd, int count); int ether_poll_register(poll_handler_t *h, if_t ifp); int ether_poll_deregister(if_t ifp); #endif /* DEVICE_POLLING */ #endif /* _KERNEL */ #include /* XXXAO: temporary unconditional include */ #endif /* !_NET_IF_VAR_H_ */ Index: projects/clang380-import/sys/net/route.h =================================================================== --- projects/clang380-import/sys/net/route.h (revision 293015) +++ projects/clang380-import/sys/net/route.h (revision 293016) @@ -1,461 +1,468 @@ /*- * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _NET_ROUTE_H_ #define _NET_ROUTE_H_ #include #include /* * Kernel resident routing tables. * * The routing tables are initialized when interface addresses * are set by making entries for all directly connected interfaces. */ /* * A route consists of a destination address, a reference * to a routing entry, and a reference to an llentry. * These are often held by protocols in their control * blocks, e.g. inpcb. */ struct route { struct rtentry *ro_rt; - struct llentry *ro_lle; - struct in_ifaddr *ro_ia; - int ro_flags; + char *ro_prepend; + uint16_t ro_plen; + uint16_t ro_flags; struct sockaddr ro_dst; }; +#define RT_L2_ME_BIT 2 /* dst L2 addr is our address */ +#define RT_MAY_LOOP_BIT 3 /* dst may require loop copy */ +#define RT_HAS_HEADER_BIT 4 /* mbuf already have its header prepended */ + #define RT_CACHING_CONTEXT 0x1 /* XXX: not used anywhere */ #define RT_NORTREF 0x2 /* doesn't hold reference on ro_rt */ +#define RT_L2_ME (1 << RT_L2_ME_BIT) +#define RT_MAY_LOOP (1 << RT_MAY_LOOP_BIT) +#define RT_HAS_HEADER (1 << RT_HAS_HEADER_BIT) struct rt_metrics { u_long rmx_locks; /* Kernel must leave these values alone */ u_long rmx_mtu; /* MTU for this path */ u_long rmx_hopcount; /* max hops expected */ u_long rmx_expire; /* lifetime for route, e.g. redirect */ u_long rmx_recvpipe; /* inbound delay-bandwidth product */ u_long rmx_sendpipe; /* outbound delay-bandwidth product */ u_long rmx_ssthresh; /* outbound gateway buffer limit */ u_long rmx_rtt; /* estimated round trip time */ u_long rmx_rttvar; /* estimated rtt variance */ u_long rmx_pksent; /* packets sent using this route */ u_long rmx_weight; /* route weight */ u_long rmx_filler[3]; /* will be used for T/TCP later */ }; /* * rmx_rtt and rmx_rttvar are stored as microseconds; * RTTTOPRHZ(rtt) converts to a value suitable for use * by a protocol slowtimo counter. */ #define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ #define RTTTOPRHZ(r) ((r) / (RTM_RTTUNIT / PR_SLOWHZ)) /* lle state is exported in rmx_state rt_metrics field */ #define rmx_state rmx_weight #define RT_DEFAULT_FIB 0 /* Explicitly mark fib=0 restricted cases */ #define RT_ALL_FIBS -1 /* Announce event for every fib */ #ifdef _KERNEL extern u_int rt_numfibs; /* number of usable routing tables */ VNET_DECLARE(u_int, rt_add_addr_allfibs); /* Announce interfaces to all fibs */ #define V_rt_add_addr_allfibs VNET(rt_add_addr_allfibs) #endif /* * We distinguish between routes to hosts and routes to networks, * preferring the former if available. For each route we infer * the interface to use from the gateway address supplied when * the route was entered. Routes that forward packets through * gateways are marked so that the output routines know to address the * gateway rather than the ultimate destination. */ #ifndef RNF_NORMAL #include #ifdef RADIX_MPATH #include #endif #endif #if defined(_KERNEL) || defined(_WANT_RTENTRY) struct rtentry { struct radix_node rt_nodes[2]; /* tree glue, and other values */ /* * XXX struct rtentry must begin with a struct radix_node (or two!) * because the code does some casts of a 'struct radix_node *' * to a 'struct rtentry *' */ #define rt_key(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_key))) #define rt_mask(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_mask))) struct sockaddr *rt_gateway; /* value */ struct ifnet *rt_ifp; /* the answer: interface to use */ struct ifaddr *rt_ifa; /* the answer: interface address to use */ int rt_flags; /* up/down?, host/net */ int rt_refcnt; /* # held references */ u_int rt_fibnum; /* which FIB */ u_long rt_mtu; /* MTU for this path */ u_long rt_weight; /* absolute weight */ u_long rt_expire; /* lifetime for route, e.g. redirect */ #define rt_endzero rt_pksent counter_u64_t rt_pksent; /* packets sent using this route */ struct mtx rt_mtx; /* mutex for routing entry */ struct rtentry *rt_chain; /* pointer to next rtentry to delete */ }; #endif /* _KERNEL || _WANT_RTENTRY */ #define RTF_UP 0x1 /* route usable */ #define RTF_GATEWAY 0x2 /* destination is a gateway */ #define RTF_HOST 0x4 /* host entry (net otherwise) */ #define RTF_REJECT 0x8 /* host or net unreachable */ #define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ #define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ #define RTF_DONE 0x40 /* message confirmed */ /* 0x80 unused, was RTF_DELCLONE */ /* 0x100 unused, was RTF_CLONING */ #define RTF_XRESOLVE 0x200 /* external daemon resolves name */ #define RTF_LLINFO 0x400 /* DEPRECATED - exists ONLY for backward compatibility */ #define RTF_LLDATA 0x400 /* used by apps to add/del L2 entries */ #define RTF_STATIC 0x800 /* manually added */ #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ /* 0x10000 unused, was RTF_PRCLONING */ /* 0x20000 unused, was RTF_WASCLONED */ #define RTF_PROTO3 0x40000 /* protocol specific routing flag */ #define RTF_FIXEDMTU 0x80000 /* MTU was explicitly specified */ #define RTF_PINNED 0x100000 /* route is immutable */ #define RTF_LOCAL 0x200000 /* route represents a local address */ #define RTF_BROADCAST 0x400000 /* route represents a bcast address */ #define RTF_MULTICAST 0x800000 /* route represents a mcast address */ /* 0x8000000 and up unassigned */ #define RTF_STICKY 0x10000000 /* always route dst->src */ #define RTF_RNH_LOCKED 0x40000000 /* radix node head is locked */ #define RTF_GWFLAG_COMPAT 0x80000000 /* a compatibility bit for interacting with existing routing apps */ /* Mask of RTF flags that are allowed to be modified by RTM_CHANGE. */ #define RTF_FMASK \ (RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \ RTF_REJECT | RTF_STATIC | RTF_STICKY) /* * fib_ nexthop API flags. */ /* Consumer-visible nexthop info flags */ #define NHF_REJECT 0x0010 /* RTF_REJECT */ #define NHF_BLACKHOLE 0x0020 /* RTF_BLACKHOLE */ #define NHF_REDIRECT 0x0040 /* RTF_DYNAMIC|RTF_MODIFIED */ #define NHF_DEFAULT 0x0080 /* Default route */ #define NHF_BROADCAST 0x0100 /* RTF_BROADCAST */ #define NHF_GATEWAY 0x0200 /* RTF_GATEWAY */ /* Nexthop request flags */ #define NHR_IFAIF 0x01 /* Return ifa_ifp interface */ #define NHR_REF 0x02 /* For future use */ /* rte<>nhop translation */ static inline uint16_t fib_rte_to_nh_flags(int rt_flags) { uint16_t res; res = (rt_flags & RTF_REJECT) ? NHF_REJECT : 0; res |= (rt_flags & RTF_BLACKHOLE) ? NHF_BLACKHOLE : 0; res |= (rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) ? NHF_REDIRECT : 0; res |= (rt_flags & RTF_BROADCAST) ? NHF_BROADCAST : 0; res |= (rt_flags & RTF_GATEWAY) ? NHF_GATEWAY : 0; return (res); } /* * Routing statistics. */ struct rtstat { short rts_badredirect; /* bogus redirect calls */ short rts_dynamic; /* routes created by redirects */ short rts_newgateway; /* routes modified by redirects */ short rts_unreach; /* lookups which failed */ short rts_wildcard; /* lookups satisfied by a wildcard */ }; /* * Structures for routing messages. */ struct rt_msghdr { u_short rtm_msglen; /* to skip over non-understood messages */ u_char rtm_version; /* future binary compatibility */ u_char rtm_type; /* message type */ u_short rtm_index; /* index for associated ifp */ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ int rtm_addrs; /* bitmask identifying sockaddrs in msg */ pid_t rtm_pid; /* identify sender */ int rtm_seq; /* for sender to identify action */ int rtm_errno; /* why failed */ int rtm_fmask; /* bitmask used in RTM_CHANGE message */ u_long rtm_inits; /* which metrics we are initializing */ struct rt_metrics rtm_rmx; /* metrics themselves */ }; #define RTM_VERSION 5 /* Up the ante and ignore older versions */ /* * Message types. */ #define RTM_ADD 0x1 /* Add Route */ #define RTM_DELETE 0x2 /* Delete Route */ #define RTM_CHANGE 0x3 /* Change Metrics or flags */ #define RTM_GET 0x4 /* Report Metrics */ #define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */ #define RTM_REDIRECT 0x6 /* Told to use different route */ #define RTM_MISS 0x7 /* Lookup failed on this address */ #define RTM_LOCK 0x8 /* fix specified metrics */ /* 0x9 */ /* 0xa */ #define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ #define RTM_NEWADDR 0xc /* address being added to iface */ #define RTM_DELADDR 0xd /* address being removed from iface */ #define RTM_IFINFO 0xe /* iface going up/down etc. */ #define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ #define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ #define RTM_IFANNOUNCE 0x11 /* iface arrival/departure */ #define RTM_IEEE80211 0x12 /* IEEE80211 wireless event */ /* * Bitmask values for rtm_inits and rmx_locks. */ #define RTV_MTU 0x1 /* init or lock _mtu */ #define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ #define RTV_EXPIRE 0x4 /* init or lock _expire */ #define RTV_RPIPE 0x8 /* init or lock _recvpipe */ #define RTV_SPIPE 0x10 /* init or lock _sendpipe */ #define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ #define RTV_RTT 0x40 /* init or lock _rtt */ #define RTV_RTTVAR 0x80 /* init or lock _rttvar */ #define RTV_WEIGHT 0x100 /* init or lock _weight */ /* * Bitmask values for rtm_addrs. */ #define RTA_DST 0x1 /* destination sockaddr present */ #define RTA_GATEWAY 0x2 /* gateway sockaddr present */ #define RTA_NETMASK 0x4 /* netmask sockaddr present */ #define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ #define RTA_IFP 0x10 /* interface name sockaddr present */ #define RTA_IFA 0x20 /* interface addr sockaddr present */ #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ /* * Index offsets for sockaddr array for alternate internal encoding. */ #define RTAX_DST 0 /* destination sockaddr present */ #define RTAX_GATEWAY 1 /* gateway sockaddr present */ #define RTAX_NETMASK 2 /* netmask sockaddr present */ #define RTAX_GENMASK 3 /* cloning mask sockaddr present */ #define RTAX_IFP 4 /* interface name sockaddr present */ #define RTAX_IFA 5 /* interface addr sockaddr present */ #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ #define RTAX_MAX 8 /* size of array to allocate */ typedef int rt_filter_f_t(const struct rtentry *, void *); struct rt_addrinfo { int rti_addrs; /* Route RTF_ flags */ int rti_flags; /* Route RTF_ flags */ struct sockaddr *rti_info[RTAX_MAX]; /* Sockaddr data */ struct ifaddr *rti_ifa; /* value of rt_ifa addr */ struct ifnet *rti_ifp; /* route interface */ rt_filter_f_t *rti_filter; /* filter function */ void *rti_filterdata; /* filter paramenters */ u_long rti_mflags; /* metrics RTV_ flags */ u_long rti_spare; /* Will be used for fib */ struct rt_metrics *rti_rmx; /* Pointer to route metrics */ }; /* * This macro returns the size of a struct sockaddr when passed * through a routing socket. Basically we round up sa_len to * a multiple of sizeof(long), with a minimum of sizeof(long). * The check for a NULL pointer is just a convenience, probably never used. * The case sa_len == 0 should only apply to empty structures. */ #define SA_SIZE(sa) \ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ sizeof(long) : \ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) #define sa_equal(a, b) ( \ (((const struct sockaddr *)(a))->sa_len == ((const struct sockaddr *)(b))->sa_len) && \ (bcmp((a), (b), ((const struct sockaddr *)(b))->sa_len) == 0)) #ifdef _KERNEL #define RT_LINK_IS_UP(ifp) (!((ifp)->if_capabilities & IFCAP_LINKSTATE) \ || (ifp)->if_link_state == LINK_STATE_UP) #define RT_LOCK_INIT(_rt) \ mtx_init(&(_rt)->rt_mtx, "rtentry", NULL, MTX_DEF | MTX_DUPOK) #define RT_LOCK(_rt) mtx_lock(&(_rt)->rt_mtx) #define RT_UNLOCK(_rt) mtx_unlock(&(_rt)->rt_mtx) #define RT_LOCK_DESTROY(_rt) mtx_destroy(&(_rt)->rt_mtx) #define RT_LOCK_ASSERT(_rt) mtx_assert(&(_rt)->rt_mtx, MA_OWNED) #define RT_UNLOCK_COND(_rt) do { \ if (mtx_owned(&(_rt)->rt_mtx)) \ mtx_unlock(&(_rt)->rt_mtx); \ } while (0) #define RT_ADDREF(_rt) do { \ RT_LOCK_ASSERT(_rt); \ KASSERT((_rt)->rt_refcnt >= 0, \ ("negative refcnt %d", (_rt)->rt_refcnt)); \ (_rt)->rt_refcnt++; \ } while (0) #define RT_REMREF(_rt) do { \ RT_LOCK_ASSERT(_rt); \ KASSERT((_rt)->rt_refcnt > 0, \ ("bogus refcnt %d", (_rt)->rt_refcnt)); \ (_rt)->rt_refcnt--; \ } while (0) #define RTFREE_LOCKED(_rt) do { \ if ((_rt)->rt_refcnt <= 1) \ rtfree(_rt); \ else { \ RT_REMREF(_rt); \ RT_UNLOCK(_rt); \ } \ /* guard against invalid refs */ \ _rt = 0; \ } while (0) #define RTFREE(_rt) do { \ RT_LOCK(_rt); \ RTFREE_LOCKED(_rt); \ } while (0) #define RO_RTFREE(_ro) do { \ if ((_ro)->ro_rt) { \ if ((_ro)->ro_flags & RT_NORTREF) { \ (_ro)->ro_flags &= ~RT_NORTREF; \ (_ro)->ro_rt = NULL; \ } else { \ RT_LOCK((_ro)->ro_rt); \ RTFREE_LOCKED((_ro)->ro_rt); \ } \ } \ } while (0) struct radix_node_head *rt_tables_get_rnh(int, int); struct ifmultiaddr; void rt_ieee80211msg(struct ifnet *, int, void *, size_t); void rt_ifannouncemsg(struct ifnet *, int); void rt_ifmsg(struct ifnet *); void rt_missmsg(int, struct rt_addrinfo *, int, int); void rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int); void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *); void rt_newaddrmsg_fib(int, struct ifaddr *, int, struct rtentry *, int); int rt_addrmsg(int, struct ifaddr *, int); int rt_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); void rt_newmaddrmsg(int, struct ifmultiaddr *); int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); int rtsock_addrmsg(int, struct ifaddr *, int); int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); /* * Note the following locking behavior: * * rtalloc_ign() and rtalloc() return ro->ro_rt unlocked * * rtalloc1() returns a locked rtentry * * rtfree() and RTFREE_LOCKED() require a locked rtentry * * RTFREE() uses an unlocked entry. */ int rt_expunge(struct radix_node_head *, struct rtentry *); void rtfree(struct rtentry *); int rt_check(struct rtentry **, struct rtentry **, struct sockaddr *); void rt_updatemtu(struct ifnet *); typedef int rt_walktree_f_t(struct rtentry *, void *); typedef void rt_setwarg_t(struct radix_node_head *, uint32_t, int, void *); void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *); void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg); void rt_flushifroutes(struct ifnet *ifp); /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ /* Thes are used by old code not yet converted to use multiple FIBS */ void rtalloc_ign(struct route *ro, u_long ignflags); void rtalloc(struct route *ro); /* XXX deprecated, use rtalloc_ign(ro, 0) */ struct rtentry *rtalloc1(struct sockaddr *, int, u_long); int rtinit(struct ifaddr *, int, int); int rtioctl(u_long, caddr_t); void rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct sockaddr *); int rtrequest(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **); /* XXX MRT NEW VERSIONS THAT USE FIBs * For now the protocol indepedent versions are the same as the AF_INET ones * but this will change.. */ int rt_getifa_fib(struct rt_addrinfo *, u_int fibnum); void rtalloc_ign_fib(struct route *ro, u_long ignflags, u_int fibnum); void rtalloc_fib(struct route *ro, u_int fibnum); struct rtentry *rtalloc1_fib(struct sockaddr *, int, u_long, u_int); int rtioctl_fib(u_long, caddr_t, u_int); void rtredirect_fib(struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct sockaddr *, u_int); int rtrequest_fib(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int); int rtrequest1_fib(int, struct rt_addrinfo *, struct rtentry **, u_int); #include typedef void (*rtevent_redirect_fn)(void *, struct rtentry *, struct rtentry *, struct sockaddr *); EVENTHANDLER_DECLARE(route_redirect_event, rtevent_redirect_fn); #endif #endif Index: projects/clang380-import/sys/netinet/if_ether.c =================================================================== --- projects/clang380-import/sys/netinet/if_ether.c (revision 293015) +++ projects/clang380-import/sys/netinet/if_ether.c (revision 293016) @@ -1,1235 +1,1341 @@ /*- * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ether.c 8.1 (Berkeley) 6/10/93 */ /* * Ethernet address resolution protocol. * TODO: * add "inuse/lock" bit (or ref. count) along with valid bit */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #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 #ifdef INET #include #endif #include #define SIN(s) ((const struct sockaddr_in *)(s)) static struct timeval arp_lastlog; static int arp_curpps; static int arp_maxpps = 1; /* Simple ARP state machine */ enum arp_llinfo_state { ARP_LLINFO_INCOMPLETE = 0, /* No LLE data */ ARP_LLINFO_REACHABLE, /* LLE is valid */ ARP_LLINFO_VERIFY, /* LLE is valid, need refresh */ ARP_LLINFO_DELETED, /* LLE is deleted */ }; SYSCTL_DECL(_net_link_ether); static SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, ""); static SYSCTL_NODE(_net_link_ether, PF_ARP, arp, CTLFLAG_RW, 0, ""); /* timer values */ static VNET_DEFINE(int, arpt_keep) = (20*60); /* once resolved, good for 20 * minutes */ static VNET_DEFINE(int, arp_maxtries) = 5; static VNET_DEFINE(int, arp_proxyall) = 0; static VNET_DEFINE(int, arpt_down) = 20; /* keep incomplete entries for * 20 seconds */ static VNET_DEFINE(int, arpt_rexmit) = 1; /* retransmit arp entries, sec*/ VNET_PCPUSTAT_DEFINE(struct arpstat, arpstat); /* ARP statistics, see if_arp.h */ VNET_PCPUSTAT_SYSINIT(arpstat); #ifdef VIMAGE VNET_PCPUSTAT_SYSUNINIT(arpstat); #endif /* VIMAGE */ static VNET_DEFINE(int, arp_maxhold) = 1; #define V_arpt_keep VNET(arpt_keep) #define V_arpt_down VNET(arpt_down) #define V_arpt_rexmit VNET(arpt_rexmit) #define V_arp_maxtries VNET(arp_maxtries) #define V_arp_proxyall VNET(arp_proxyall) #define V_arp_maxhold VNET(arp_maxhold) SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arpt_keep), 0, "ARP entry lifetime in seconds"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arp_maxtries), 0, "ARP resolution attempts before returning error"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arp_proxyall), 0, "Enable proxy ARP for all suitable requests"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, wait, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arpt_down), 0, "Incomplete ARP entry lifetime in seconds"); SYSCTL_VNET_PCPUSTAT(_net_link_ether_arp, OID_AUTO, stats, struct arpstat, arpstat, "ARP statistics (struct arpstat, net/if_arp.h)"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxhold, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(arp_maxhold), 0, "Number of packets to hold per ARP entry"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_log_per_second, CTLFLAG_RW, &arp_maxpps, 0, "Maximum number of remotely triggered ARP messages that can be " "logged per second"); #define ARP_LOG(pri, ...) do { \ if (ppsratecheck(&arp_lastlog, &arp_curpps, arp_maxpps)) \ log((pri), "arp: " __VA_ARGS__); \ } while (0) static void arp_init(void); static void arpintr(struct mbuf *); static void arptimer(void *); #ifdef INET static void in_arpinput(struct mbuf *); #endif static void arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr, struct ifnet *ifp, int bridged, struct llentry *la); static void arp_mark_lle_reachable(struct llentry *la); static void arp_iflladdr(void *arg __unused, struct ifnet *ifp); static eventhandler_tag iflladdr_tag; static const struct netisr_handler arp_nh = { .nh_name = "arp", .nh_handler = arpintr, .nh_proto = NETISR_ARP, .nh_policy = NETISR_POLICY_SOURCE, }; /* * Timeout routine. Age arp_tab entries periodically. */ static void arptimer(void *arg) { struct llentry *lle = (struct llentry *)arg; struct ifnet *ifp; int r_skip_req; if (lle->la_flags & LLE_STATIC) { return; } LLE_WLOCK(lle); if (callout_pending(&lle->lle_timer)) { /* * Here we are a bit odd here in the treatment of * active/pending. If the pending bit is set, it got * rescheduled before I ran. The active * bit we ignore, since if it was stopped * in ll_tablefree() and was currently running * it would have return 0 so the code would * not have deleted it since the callout could * not be stopped so we want to go through * with the delete here now. If the callout * was restarted, the pending bit will be back on and * we just want to bail since the callout_reset would * return 1 and our reference would have been removed * by arpresolve() below. */ LLE_WUNLOCK(lle); return; } ifp = lle->lle_tbl->llt_ifp; CURVNET_SET(ifp->if_vnet); switch (lle->ln_state) { case ARP_LLINFO_REACHABLE: /* * Expiration time is approaching. * Let's try to refresh entry if it is still * in use. * * Set r_skip_req to get feedback from * fast path. Change state and re-schedule * ourselves. */ LLE_REQ_LOCK(lle); lle->r_skip_req = 1; LLE_REQ_UNLOCK(lle); lle->ln_state = ARP_LLINFO_VERIFY; callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); LLE_WUNLOCK(lle); CURVNET_RESTORE(); return; case ARP_LLINFO_VERIFY: LLE_REQ_LOCK(lle); r_skip_req = lle->r_skip_req; LLE_REQ_UNLOCK(lle); if (r_skip_req == 0 && lle->la_preempt > 0) { /* Entry was used, issue refresh request */ struct in_addr dst; dst = lle->r_l3addr.addr4; lle->la_preempt--; callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); LLE_WUNLOCK(lle); arprequest(ifp, NULL, &dst, NULL); CURVNET_RESTORE(); return; } /* Nothing happened. Reschedule if not too late */ if (lle->la_expire > time_uptime) { callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); LLE_WUNLOCK(lle); CURVNET_RESTORE(); return; } break; case ARP_LLINFO_INCOMPLETE: case ARP_LLINFO_DELETED: break; } if ((lle->la_flags & LLE_DELETED) == 0) { int evt; if (lle->la_flags & LLE_VALID) evt = LLENTRY_EXPIRED; else evt = LLENTRY_TIMEDOUT; EVENTHANDLER_INVOKE(lle_event, lle, evt); } callout_stop(&lle->lle_timer); /* XXX: LOR avoidance. We still have ref on lle. */ LLE_WUNLOCK(lle); IF_AFDATA_LOCK(ifp); LLE_WLOCK(lle); /* Guard against race with other llentry_free(). */ if (lle->la_flags & LLE_LINKED) { LLE_REMREF(lle); lltable_unlink_entry(lle->lle_tbl, lle); } IF_AFDATA_UNLOCK(ifp); size_t pkts_dropped = llentry_free(lle); ARPSTAT_ADD(dropped, pkts_dropped); ARPSTAT_INC(timeouts); CURVNET_RESTORE(); } /* + * Stores link-layer header for @ifp in format suitable for if_output() + * into buffer @buf. Resulting header length is stored in @bufsize. + * + * Returns 0 on success. + */ +static int +arp_fillheader(struct ifnet *ifp, struct arphdr *ah, int bcast, u_char *buf, + size_t *bufsize) +{ + struct if_encap_req ereq; + int error; + + bzero(buf, *bufsize); + bzero(&ereq, sizeof(ereq)); + ereq.buf = buf; + ereq.bufsize = *bufsize; + ereq.rtype = IFENCAP_LL; + ereq.family = AF_ARP; + ereq.lladdr = ar_tha(ah); + ereq.hdata = (u_char *)ah; + if (bcast) + ereq.flags = IFENCAP_FLAG_BROADCAST; + error = ifp->if_requestencap(ifp, &ereq); + if (error == 0) + *bufsize = ereq.bufsize; + + return (error); +} + + +/* * Broadcast an ARP request. Caller specifies: * - arp header source ip address * - arp header target ip address * - arp header source ethernet address */ void arprequest(struct ifnet *ifp, const struct in_addr *sip, const struct in_addr *tip, u_char *enaddr) { struct mbuf *m; struct arphdr *ah; struct sockaddr sa; u_char *carpaddr = NULL; + uint8_t linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + struct route ro; + int error; if (sip == NULL) { /* * The caller did not supply a source address, try to find * a compatible one among those assigned to this interface. */ struct ifaddr *ifa; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET) continue; if (ifa->ifa_carp) { if ((*carp_iamatch_p)(ifa, &carpaddr) == 0) continue; sip = &IA_SIN(ifa)->sin_addr; } else { carpaddr = NULL; sip = &IA_SIN(ifa)->sin_addr; } if (0 == ((sip->s_addr ^ tip->s_addr) & IA_MASKSIN(ifa)->sin_addr.s_addr)) break; /* found it. */ } IF_ADDR_RUNLOCK(ifp); if (sip == NULL) { printf("%s: cannot find matching address\n", __func__); return; } } if (enaddr == NULL) enaddr = carpaddr ? carpaddr : (u_char *)IF_LLADDR(ifp); if ((m = m_gethdr(M_NOWAIT, MT_DATA)) == NULL) return; m->m_len = sizeof(*ah) + 2 * sizeof(struct in_addr) + 2 * ifp->if_addrlen; m->m_pkthdr.len = m->m_len; M_ALIGN(m, m->m_len); ah = mtod(m, struct arphdr *); bzero((caddr_t)ah, m->m_len); #ifdef MAC mac_netinet_arp_send(ifp, m); #endif ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ifp->if_addrlen; /* hardware address length */ ah->ar_pln = sizeof(struct in_addr); /* protocol address length */ ah->ar_op = htons(ARPOP_REQUEST); bcopy(enaddr, ar_sha(ah), ah->ar_hln); bcopy(sip, ar_spa(ah), ah->ar_pln); bcopy(tip, ar_tpa(ah), ah->ar_pln); sa.sa_family = AF_ARP; sa.sa_len = 2; + + /* Calculate link header for sending frame */ + bzero(&ro, sizeof(ro)); + linkhdrsize = sizeof(linkhdr); + error = arp_fillheader(ifp, ah, 1, linkhdr, &linkhdrsize); + if (error != 0 && error != EAFNOSUPPORT) { + ARP_LOG(LOG_ERR, "Failed to calculate ARP header on %s: %d\n", + if_name(ifp), error); + return; + } + + ro.ro_prepend = linkhdr; + ro.ro_plen = linkhdrsize; + ro.ro_flags = 0; + m->m_flags |= M_BCAST; m_clrprotoflags(m); /* Avoid confusing lower layers. */ - (*ifp->if_output)(ifp, m, &sa, NULL); + (*ifp->if_output)(ifp, m, &sa, &ro); ARPSTAT_INC(txrequests); } + /* * Resolve an IP address into an ethernet address - heavy version. * Used internally by arpresolve(). * We have already checked than we can't use existing lle without * modification so we have to acquire LLE_EXCLUSIVE lle lock. * * On success, desten and flags are filled in and the function returns 0; * If the packet must be held pending resolution, we return EWOULDBLOCK * On other errors, we return the corresponding error code. * Note that m_freem() handles NULL. */ static int -arpresolve_full(struct ifnet *ifp, int is_gw, int create, struct mbuf *m, +arpresolve_full(struct ifnet *ifp, int is_gw, int flags, struct mbuf *m, const struct sockaddr *dst, u_char *desten, uint32_t *pflags) { struct llentry *la = NULL, *la_tmp; struct mbuf *curr = NULL; struct mbuf *next = NULL; int error, renew; + char *lladdr; + int ll_len; if (pflags != NULL) *pflags = 0; - if (create == 0) { + if ((flags & LLE_CREATE) == 0) { IF_AFDATA_RLOCK(ifp); la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); IF_AFDATA_RUNLOCK(ifp); } if (la == NULL && (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { la = lltable_alloc_entry(LLTABLE(ifp), 0, dst); if (la == NULL) { log(LOG_DEBUG, "arpresolve: can't allocate llinfo for %s on %s\n", inet_ntoa(SIN(dst)->sin_addr), if_name(ifp)); m_freem(m); return (EINVAL); } IF_AFDATA_WLOCK(ifp); LLE_WLOCK(la); la_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); /* Prefer ANY existing lle over newly-created one */ if (la_tmp == NULL) lltable_link_entry(LLTABLE(ifp), la); IF_AFDATA_WUNLOCK(ifp); if (la_tmp != NULL) { lltable_free_entry(LLTABLE(ifp), la); la = la_tmp; } } if (la == NULL) { m_freem(m); return (EINVAL); } if ((la->la_flags & LLE_VALID) && ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) { - bcopy(&la->ll_addr, desten, ifp->if_addrlen); + if (flags & LLE_ADDRONLY) { + lladdr = la->ll_addr; + ll_len = ifp->if_addrlen; + } else { + lladdr = la->r_linkdata; + ll_len = la->r_hdrlen; + } + bcopy(lladdr, desten, ll_len); /* Check if we have feedback request from arptimer() */ if (la->r_skip_req != 0) { LLE_REQ_LOCK(la); la->r_skip_req = 0; /* Notify that entry was used */ LLE_REQ_UNLOCK(la); } if (pflags != NULL) *pflags = la->la_flags & (LLE_VALID|LLE_IFADDR); LLE_WUNLOCK(la); return (0); } renew = (la->la_asked == 0 || la->la_expire != time_uptime); /* * There is an arptab entry, but no ethernet address * response yet. Add the mbuf to the list, dropping * the oldest packet if we have exceeded the system * setting. */ if (m != NULL) { if (la->la_numheld >= V_arp_maxhold) { if (la->la_hold != NULL) { next = la->la_hold->m_nextpkt; m_freem(la->la_hold); la->la_hold = next; la->la_numheld--; ARPSTAT_INC(dropped); } } if (la->la_hold != NULL) { curr = la->la_hold; while (curr->m_nextpkt != NULL) curr = curr->m_nextpkt; curr->m_nextpkt = m; } else la->la_hold = m; la->la_numheld++; } /* * Return EWOULDBLOCK if we have tried less than arp_maxtries. It * will be masked by ether_output(). Return EHOSTDOWN/EHOSTUNREACH * if we have already sent arp_maxtries ARP requests. Retransmit the * ARP request, but not faster than one request per second. */ if (la->la_asked < V_arp_maxtries) error = EWOULDBLOCK; /* First request. */ else error = is_gw != 0 ? EHOSTUNREACH : EHOSTDOWN; if (renew) { int canceled; LLE_ADDREF(la); la->la_expire = time_uptime; canceled = callout_reset(&la->lle_timer, hz * V_arpt_down, arptimer, la); if (canceled) LLE_REMREF(la); la->la_asked++; LLE_WUNLOCK(la); arprequest(ifp, NULL, &SIN(dst)->sin_addr, NULL); return (error); } LLE_WUNLOCK(la); return (error); } /* * Resolve an IP address into an ethernet address. + */ +int +arpresolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, + char *desten, uint32_t *pflags) +{ + int error; + + flags |= LLE_ADDRONLY; + error = arpresolve_full(ifp, 0, flags, NULL, dst, desten, pflags); + return (error); +} + + +/* + * Lookups link header based on an IP address. * On input: * ifp is the interface we use * is_gw != 0 if @dst represents gateway to some destination * m is the mbuf. May be NULL if we don't have a packet. * dst is the next hop, - * desten is the storage to put LL address. + * desten is the storage to put LL header. * flags returns subset of lle flags: LLE_VALID | LLE_IFADDR * - * On success, desten and flags are filled in and the function returns 0; + * On success, full/partial link header and flags are filled in and + * the function returns 0. * If the packet must be held pending resolution, we return EWOULDBLOCK * On other errors, we return the corresponding error code. * Note that m_freem() handles NULL. */ int arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, const struct sockaddr *dst, u_char *desten, uint32_t *pflags) { struct llentry *la = 0; if (pflags != NULL) *pflags = 0; if (m != NULL) { if (m->m_flags & M_BCAST) { /* broadcast */ (void)memcpy(desten, ifp->if_broadcastaddr, ifp->if_addrlen); return (0); } if (m->m_flags & M_MCAST) { /* multicast */ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten); return (0); } } IF_AFDATA_RLOCK(ifp); la = lla_lookup(LLTABLE(ifp), LLE_UNLOCKED, dst); if (la != NULL && (la->r_flags & RLLE_VALID) != 0) { /* Entry found, let's copy lle info */ - bcopy(&la->ll_addr, desten, ifp->if_addrlen); + bcopy(la->r_linkdata, desten, la->r_hdrlen); if (pflags != NULL) *pflags = LLE_VALID | (la->r_flags & RLLE_IFADDR); /* Check if we have feedback request from arptimer() */ if (la->r_skip_req != 0) { LLE_REQ_LOCK(la); la->r_skip_req = 0; /* Notify that entry was used */ LLE_REQ_UNLOCK(la); } IF_AFDATA_RUNLOCK(ifp); return (0); } IF_AFDATA_RUNLOCK(ifp); - return (arpresolve_full(ifp, is_gw, 1, m, dst, desten, pflags)); + return (arpresolve_full(ifp, is_gw, la == NULL ? LLE_CREATE : 0, m, dst, + desten, pflags)); } /* * Common length and type checks are done here, * then the protocol-specific routine is called. */ static void arpintr(struct mbuf *m) { struct arphdr *ar; struct ifnet *ifp; char *layer; int hlen; ifp = m->m_pkthdr.rcvif; if (m->m_len < sizeof(struct arphdr) && ((m = m_pullup(m, sizeof(struct arphdr))) == NULL)) { ARP_LOG(LOG_NOTICE, "packet with short header received on %s\n", if_name(ifp)); return; } ar = mtod(m, struct arphdr *); /* Check if length is sufficient */ if (m->m_len < arphdr_len(ar)) { m = m_pullup(m, arphdr_len(ar)); if (m == NULL) { ARP_LOG(LOG_NOTICE, "short packet received on %s\n", if_name(ifp)); return; } ar = mtod(m, struct arphdr *); } hlen = 0; layer = ""; switch (ntohs(ar->ar_hrd)) { case ARPHRD_ETHER: hlen = ETHER_ADDR_LEN; /* RFC 826 */ layer = "ethernet"; break; case ARPHRD_IEEE802: hlen = 6; /* RFC 1390, FDDI_ADDR_LEN */ layer = "fddi"; break; case ARPHRD_ARCNET: hlen = 1; /* RFC 1201, ARC_ADDR_LEN */ layer = "arcnet"; break; case ARPHRD_INFINIBAND: hlen = 20; /* RFC 4391, INFINIBAND_ALEN */ layer = "infiniband"; break; case ARPHRD_IEEE1394: hlen = 0; /* SHALL be 16 */ /* RFC 2734 */ layer = "firewire"; /* * Restrict too long hardware addresses. * Currently we are capable of handling 20-byte * addresses ( sizeof(lle->ll_addr) ) */ if (ar->ar_hln >= 20) hlen = 16; break; default: ARP_LOG(LOG_NOTICE, "packet with unknown hardware format 0x%02d received on " "%s\n", ntohs(ar->ar_hrd), if_name(ifp)); m_freem(m); return; } if (hlen != 0 && hlen != ar->ar_hln) { ARP_LOG(LOG_NOTICE, "packet with invalid %s address length %d received on %s\n", layer, ar->ar_hln, if_name(ifp)); m_freem(m); return; } ARPSTAT_INC(received); switch (ntohs(ar->ar_pro)) { #ifdef INET case ETHERTYPE_IP: in_arpinput(m); return; #endif } m_freem(m); } #ifdef INET /* * ARP for Internet protocols on 10 Mb/s Ethernet. * Algorithm is that given in RFC 826. * In addition, a sanity check is performed on the sender * protocol address, to catch impersonators. * We no longer handle negotiations for use of trailer protocol: * Formerly, ARP replied for protocol type ETHERTYPE_TRAIL sent * along with IP replies if we wanted trailers sent to us, * and also sent them in response to IP replies. * This allowed either end to announce the desire to receive * trailer packets. * We no longer reply to requests for ETHERTYPE_TRAIL protocol either, * but formerly didn't normally send requests. */ static int log_arp_wrong_iface = 1; static int log_arp_movements = 1; static int log_arp_permanent_modify = 1; static int allow_multicast = 0; SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_wrong_iface, CTLFLAG_RW, &log_arp_wrong_iface, 0, "log arp packets arriving on the wrong interface"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_movements, CTLFLAG_RW, &log_arp_movements, 0, "log arp replies from MACs different than the one in the cache"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_permanent_modify, CTLFLAG_RW, &log_arp_permanent_modify, 0, "log arp replies from MACs different than the one in the permanent arp entry"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, allow_multicast, CTLFLAG_RW, &allow_multicast, 0, "accept multicast addresses"); static void in_arpinput(struct mbuf *m) { struct rm_priotracker in_ifa_tracker; struct arphdr *ah; struct ifnet *ifp = m->m_pkthdr.rcvif; struct llentry *la = NULL, *la_tmp; struct ifaddr *ifa; struct in_ifaddr *ia; struct sockaddr sa; struct in_addr isaddr, itaddr, myaddr; u_int8_t *enaddr = NULL; int op; int bridged = 0, is_bridge = 0; int carped; struct sockaddr_in sin; struct sockaddr *dst; struct nhop4_basic nh4; + uint8_t linkhdr[LLE_MAX_LINKHDR]; + struct route ro; + size_t linkhdrsize; + int lladdr_off; + int error; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; if (ifp->if_bridge) bridged = 1; if (ifp->if_type == IFT_BRIDGE) is_bridge = 1; /* * We already have checked that mbuf contains enough contiguous data * to hold entire arp message according to the arp header. */ ah = mtod(m, struct arphdr *); /* * ARP is only for IPv4 so we can reject packets with * a protocol length not equal to an IPv4 address. */ if (ah->ar_pln != sizeof(struct in_addr)) { ARP_LOG(LOG_NOTICE, "requested protocol length != %zu\n", sizeof(struct in_addr)); goto drop; } if (allow_multicast == 0 && ETHER_IS_MULTICAST(ar_sha(ah))) { ARP_LOG(LOG_NOTICE, "%*D is multicast\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":"); goto drop; } op = ntohs(ah->ar_op); (void)memcpy(&isaddr, ar_spa(ah), sizeof (isaddr)); (void)memcpy(&itaddr, ar_tpa(ah), sizeof (itaddr)); if (op == ARPOP_REPLY) ARPSTAT_INC(rxreplies); /* * For a bridge, we want to check the address irrespective * of the receive interface. (This will change slightly * when we have clusters of interfaces). */ IN_IFADDR_RLOCK(&in_ifa_tracker); LIST_FOREACH(ia, INADDR_HASH(itaddr.s_addr), ia_hash) { if (((bridged && ia->ia_ifp->if_bridge == ifp->if_bridge) || ia->ia_ifp == ifp) && itaddr.s_addr == ia->ia_addr.sin_addr.s_addr && (ia->ia_ifa.ifa_carp == NULL || (*carp_iamatch_p)(&ia->ia_ifa, &enaddr))) { ifa_ref(&ia->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); goto match; } } LIST_FOREACH(ia, INADDR_HASH(isaddr.s_addr), ia_hash) if (((bridged && ia->ia_ifp->if_bridge == ifp->if_bridge) || ia->ia_ifp == ifp) && isaddr.s_addr == ia->ia_addr.sin_addr.s_addr) { ifa_ref(&ia->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); goto match; } #define BDG_MEMBER_MATCHES_ARP(addr, ifp, ia) \ (ia->ia_ifp->if_bridge == ifp->if_softc && \ !bcmp(IF_LLADDR(ia->ia_ifp), IF_LLADDR(ifp), ifp->if_addrlen) && \ addr == ia->ia_addr.sin_addr.s_addr) /* * Check the case when bridge shares its MAC address with * some of its children, so packets are claimed by bridge * itself (bridge_input() does it first), but they are really * meant to be destined to the bridge member. */ if (is_bridge) { LIST_FOREACH(ia, INADDR_HASH(itaddr.s_addr), ia_hash) { if (BDG_MEMBER_MATCHES_ARP(itaddr.s_addr, ifp, ia)) { ifa_ref(&ia->ia_ifa); ifp = ia->ia_ifp; IN_IFADDR_RUNLOCK(&in_ifa_tracker); goto match; } } } #undef BDG_MEMBER_MATCHES_ARP IN_IFADDR_RUNLOCK(&in_ifa_tracker); /* * No match, use the first inet address on the receive interface * as a dummy address for the rest of the function. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_carp == NULL || (*carp_iamatch_p)(ifa, &enaddr))) { ia = ifatoia(ifa); ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); goto match; } IF_ADDR_RUNLOCK(ifp); /* * If bridging, fall back to using any inet address. */ IN_IFADDR_RLOCK(&in_ifa_tracker); if (!bridged || (ia = TAILQ_FIRST(&V_in_ifaddrhead)) == NULL) { IN_IFADDR_RUNLOCK(&in_ifa_tracker); goto drop; } ifa_ref(&ia->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); match: if (!enaddr) enaddr = (u_int8_t *)IF_LLADDR(ifp); carped = (ia->ia_ifa.ifa_carp != NULL); myaddr = ia->ia_addr.sin_addr; ifa_free(&ia->ia_ifa); if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) goto drop; /* it's from me, ignore it. */ if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { ARP_LOG(LOG_NOTICE, "link address is broadcast for IP address " "%s!\n", inet_ntoa(isaddr)); goto drop; } if (ifp->if_addrlen != ah->ar_hln) { ARP_LOG(LOG_WARNING, "from %*D: addr len: new %d, " "i/f %d (ignored)\n", ifp->if_addrlen, (u_char *) ar_sha(ah), ":", ah->ar_hln, ifp->if_addrlen); goto drop; } /* * Warn if another host is using the same IP address, but only if the * IP address isn't 0.0.0.0, which is used for DHCP only, in which * case we suppress the warning to avoid false positive complaints of * potential misconfiguration. */ if (!bridged && !carped && isaddr.s_addr == myaddr.s_addr && myaddr.s_addr != 0) { ARP_LOG(LOG_ERR, "%*D is using my IP address %s on %s!\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr), ifp->if_xname); itaddr = myaddr; ARPSTAT_INC(dupips); goto reply; } if (ifp->if_flags & IFF_STATICARP) goto reply; bzero(&sin, sizeof(sin)); sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr = isaddr; dst = (struct sockaddr *)&sin; IF_AFDATA_RLOCK(ifp); la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); IF_AFDATA_RUNLOCK(ifp); if (la != NULL) arp_check_update_lle(ah, isaddr, ifp, bridged, la); else if (itaddr.s_addr == myaddr.s_addr) { /* * Request/reply to our address, but no lle exists yet. - * Try to create new llentry. + * Calculate full link prepend to use in lle. */ + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET, ar_sha(ah), linkhdr, + &linkhdrsize, &lladdr_off) != 0) + goto reply; + + /* Allocate new entry */ la = lltable_alloc_entry(LLTABLE(ifp), 0, dst); if (la == NULL) { /* * lle creation may fail if source address belongs * to non-directly connected subnet. However, we * will try to answer the request instead of dropping * frame. */ goto reply; } - lltable_set_entry_addr(ifp, la, ar_sha(ah)); + lltable_set_entry_addr(ifp, la, linkhdr, linkhdrsize, + lladdr_off); IF_AFDATA_WLOCK(ifp); LLE_WLOCK(la); la_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); /* * Check if lle still does not exists. * If it does, that means that we either * 1) have configured it explicitly, via * 1a) 'arp -s' static entry or * 1b) interface address static record * or * 2) it was the result of sending first packet to-host * or * 3) it was another arp reply packet we handled in * different thread. * * In all cases except 3) we definitely need to prefer * existing lle. For the sake of simplicity, prefer any * existing lle over newly-create one. */ if (la_tmp == NULL) lltable_link_entry(LLTABLE(ifp), la); IF_AFDATA_WUNLOCK(ifp); if (la_tmp == NULL) { arp_mark_lle_reachable(la); LLE_WUNLOCK(la); } else { /* Free newly-create entry and handle packet */ lltable_free_entry(LLTABLE(ifp), la); la = la_tmp; la_tmp = NULL; arp_check_update_lle(ah, isaddr, ifp, bridged, la); /* arp_check_update_lle() returns @la unlocked */ } la = NULL; } reply: if (op != ARPOP_REQUEST) goto drop; ARPSTAT_INC(rxrequests); if (itaddr.s_addr == myaddr.s_addr) { /* Shortcut.. the receiving interface is the target. */ (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); (void)memcpy(ar_sha(ah), enaddr, ah->ar_hln); } else { struct llentry *lle = NULL; sin.sin_addr = itaddr; IF_AFDATA_RLOCK(ifp); lle = lla_lookup(LLTABLE(ifp), 0, (struct sockaddr *)&sin); IF_AFDATA_RUNLOCK(ifp); if ((lle != NULL) && (lle->la_flags & LLE_PUB)) { (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); - (void)memcpy(ar_sha(ah), &lle->ll_addr, ah->ar_hln); + (void)memcpy(ar_sha(ah), lle->ll_addr, ah->ar_hln); LLE_RUNLOCK(lle); } else { if (lle != NULL) LLE_RUNLOCK(lle); if (!V_arp_proxyall) goto drop; /* XXX MRT use table 0 for arp reply */ if (fib4_lookup_nh_basic(0, itaddr, 0, 0, &nh4) != 0) goto drop; /* * Don't send proxies for nodes on the same interface * as this one came out of, or we'll get into a fight * over who claims what Ether address. */ if (nh4.nh_ifp == ifp) goto drop; (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); (void)memcpy(ar_sha(ah), enaddr, ah->ar_hln); /* * Also check that the node which sent the ARP packet * is on the interface we expect it to be on. This * avoids ARP chaos if an interface is connected to the * wrong network. */ /* XXX MRT use table 0 for arp checks */ if (fib4_lookup_nh_basic(0, isaddr, 0, 0, &nh4) != 0) goto drop; if (nh4.nh_ifp != ifp) { ARP_LOG(LOG_INFO, "proxy: ignoring request" " from %s via %s\n", inet_ntoa(isaddr), ifp->if_xname); goto drop; } #ifdef DEBUG_PROXY printf("arp: proxying for %s\n", inet_ntoa(itaddr)); #endif } } if (itaddr.s_addr == myaddr.s_addr && IN_LINKLOCAL(ntohl(itaddr.s_addr))) { /* RFC 3927 link-local IPv4; always reply by broadcast. */ #ifdef DEBUG_LINKLOCAL printf("arp: sending reply for link-local addr %s\n", inet_ntoa(itaddr)); #endif m->m_flags |= M_BCAST; m->m_flags &= ~M_MCAST; } else { /* default behaviour; never reply by broadcast. */ m->m_flags &= ~(M_BCAST|M_MCAST); } (void)memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); (void)memcpy(ar_spa(ah), &itaddr, ah->ar_pln); ah->ar_op = htons(ARPOP_REPLY); ah->ar_pro = htons(ETHERTYPE_IP); /* let's be sure! */ m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = NULL; sa.sa_family = AF_ARP; sa.sa_len = 2; + + /* Calculate link header for sending frame */ + bzero(&ro, sizeof(ro)); + linkhdrsize = sizeof(linkhdr); + error = arp_fillheader(ifp, ah, 0, linkhdr, &linkhdrsize); + + /* + * arp_fillheader() may fail due to lack of support inside encap request + * routing. This is not necessary an error, AF_ARP can/should be handled + * by if_output(). + */ + if (error != 0 && error != EAFNOSUPPORT) { + ARP_LOG(LOG_ERR, "Failed to calculate ARP header on %s: %d\n", + if_name(ifp), error); + return; + } + + ro.ro_prepend = linkhdr; + ro.ro_plen = linkhdrsize; + ro.ro_flags = 0; + m_clrprotoflags(m); /* Avoid confusing lower layers. */ - (*ifp->if_output)(ifp, m, &sa, NULL); + (*ifp->if_output)(ifp, m, &sa, &ro); ARPSTAT_INC(txreplies); return; drop: m_freem(m); } #endif /* * Checks received arp data against existing @la. * Updates lle state/performs notification if necessary. */ static void arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr, struct ifnet *ifp, int bridged, struct llentry *la) { struct sockaddr sa; struct mbuf *m_hold, *m_hold_next; + uint8_t linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; LLE_WLOCK_ASSERT(la); /* the following is not an error when doing bridging */ if (!bridged && la->lle_tbl->llt_ifp != ifp) { if (log_arp_wrong_iface) ARP_LOG(LOG_WARNING, "%s is on %s " "but got reply from %*D on %s\n", inet_ntoa(isaddr), la->lle_tbl->llt_ifp->if_xname, ifp->if_addrlen, (u_char *)ar_sha(ah), ":", ifp->if_xname); LLE_WUNLOCK(la); return; } if ((la->la_flags & LLE_VALID) && - bcmp(ar_sha(ah), &la->ll_addr, ifp->if_addrlen)) { + bcmp(ar_sha(ah), la->ll_addr, ifp->if_addrlen)) { if (la->la_flags & LLE_STATIC) { LLE_WUNLOCK(la); if (log_arp_permanent_modify) ARP_LOG(LOG_ERR, "%*D attempts to modify " "permanent entry for %s on %s\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr), ifp->if_xname); return; } if (log_arp_movements) { ARP_LOG(LOG_INFO, "%s moved from %*D " "to %*D on %s\n", inet_ntoa(isaddr), ifp->if_addrlen, (u_char *)&la->ll_addr, ":", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", ifp->if_xname); } } + /* Calculate full link prepend to use in lle */ + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET, ar_sha(ah), linkhdr, + &linkhdrsize, &lladdr_off) != 0) + return; + /* Check if something has changed */ - if (memcmp(&la->ll_addr, ar_sha(ah), ifp->if_addrlen) != 0 || + if (memcmp(la->r_linkdata, linkhdr, linkhdrsize) != 0 || (la->la_flags & LLE_VALID) == 0) { - /* Perform real LLE update */ - /* use afdata WLOCK to update fields */ - LLE_ADDREF(la); - LLE_WUNLOCK(la); - IF_AFDATA_WLOCK(ifp); - LLE_WLOCK(la); - - /* - * Since we droppped LLE lock, other thread might have deleted - * this lle. Check and return - */ - if ((la->la_flags & LLE_DELETED) != 0) { - IF_AFDATA_WUNLOCK(ifp); - LLE_FREE_LOCKED(la); + /* Try to perform LLE update */ + if (lltable_try_set_entry_addr(ifp, la, linkhdr, linkhdrsize, + lladdr_off) == 0) return; - } - /* Update data */ - lltable_set_entry_addr(ifp, la, ar_sha(ah)); - - IF_AFDATA_WUNLOCK(ifp); - LLE_REMREF(la); - /* Clear fast path feedback request if set */ la->r_skip_req = 0; } arp_mark_lle_reachable(la); /* * The packets are all freed within the call to the output * routine. * * NB: The lock MUST be released before the call to the * output routine. */ if (la->la_hold != NULL) { m_hold = la->la_hold; la->la_hold = NULL; la->la_numheld = 0; lltable_fill_sa_entry(la, &sa); LLE_WUNLOCK(la); for (; m_hold != NULL; m_hold = m_hold_next) { m_hold_next = m_hold->m_nextpkt; m_hold->m_nextpkt = NULL; /* Avoid confusing lower layers. */ m_clrprotoflags(m_hold); (*ifp->if_output)(ifp, m_hold, &sa, NULL); } } else LLE_WUNLOCK(la); } static void arp_mark_lle_reachable(struct llentry *la) { int canceled, wtime; LLE_WLOCK_ASSERT(la); la->ln_state = ARP_LLINFO_REACHABLE; EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED); if (!(la->la_flags & LLE_STATIC)) { LLE_ADDREF(la); la->la_expire = time_uptime + V_arpt_keep; wtime = V_arpt_keep - V_arp_maxtries * V_arpt_rexmit; if (wtime < 0) wtime = V_arpt_keep; canceled = callout_reset(&la->lle_timer, hz * wtime, arptimer, la); if (canceled) LLE_REMREF(la); } la->la_asked = 0; la->la_preempt = V_arp_maxtries; } /* * Add pernament link-layer record for given interface address. */ static __noinline void arp_add_ifa_lle(struct ifnet *ifp, const struct sockaddr *dst) { struct llentry *lle, *lle_tmp; /* * Interface address LLE record is considered static * because kernel code relies on LLE_STATIC flag to check * if these entries can be rewriten by arp updates. */ lle = lltable_alloc_entry(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC, dst); if (lle == NULL) { log(LOG_INFO, "arp_ifinit: cannot create arp " "entry for interface address\n"); return; } IF_AFDATA_WLOCK(ifp); LLE_WLOCK(lle); /* Unlink any entry if exists */ lle_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); if (lle_tmp != NULL) lltable_unlink_entry(LLTABLE(ifp), lle_tmp); lltable_link_entry(LLTABLE(ifp), lle); IF_AFDATA_WUNLOCK(ifp); if (lle_tmp != NULL) EVENTHANDLER_INVOKE(lle_event, lle_tmp, LLENTRY_EXPIRED); EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED); LLE_WUNLOCK(lle); if (lle_tmp != NULL) lltable_free_entry(LLTABLE(ifp), lle_tmp); } void arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) { const struct sockaddr_in *dst_in; const struct sockaddr *dst; if (ifa->ifa_carp != NULL) return; dst = ifa->ifa_addr; dst_in = (const struct sockaddr_in *)dst; if (ntohl(dst_in->sin_addr.s_addr) == INADDR_ANY) return; arp_announce_ifaddr(ifp, dst_in->sin_addr, IF_LLADDR(ifp)); arp_add_ifa_lle(ifp, dst); } void arp_announce_ifaddr(struct ifnet *ifp, struct in_addr addr, u_char *enaddr) { if (ntohl(addr.s_addr) != INADDR_ANY) arprequest(ifp, &addr, &addr, enaddr); } /* * Sends gratuitous ARPs for each ifaddr to notify other * nodes about the address change. */ static __noinline void arp_handle_ifllchange(struct ifnet *ifp) { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET) arp_ifinit(ifp, ifa); } } /* * A handler for interface link layer address change event. */ -static __noinline void +static void arp_iflladdr(void *arg __unused, struct ifnet *ifp) { + lltable_update_ifaddr(LLTABLE(ifp)); + if ((ifp->if_flags & IFF_UP) != 0) arp_handle_ifllchange(ifp); } static void arp_init(void) { netisr_register(&arp_nh); + if (IS_DEFAULT_VNET(curvnet)) + iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, + arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); if (IS_DEFAULT_VNET(curvnet)) iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); } SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); Index: projects/clang380-import/sys/netinet/if_ether.h =================================================================== --- projects/clang380-import/sys/netinet/if_ether.h (revision 293015) +++ projects/clang380-import/sys/netinet/if_ether.h (revision 293016) @@ -1,125 +1,127 @@ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ether.h 8.3 (Berkeley) 5/2/95 * $FreeBSD$ */ #ifndef _NETINET_IF_ETHER_H_ #define _NETINET_IF_ETHER_H_ #include #include /* * Macro to map an IP multicast address to an Ethernet multicast address. * The high-order 25 bits of the Ethernet address are statically assigned, * and the low-order 23 bits are taken from the low end of the IP address. */ #define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \ /* struct in_addr *ipaddr; */ \ /* u_char enaddr[ETHER_ADDR_LEN]; */ \ { \ (enaddr)[0] = 0x01; \ (enaddr)[1] = 0x00; \ (enaddr)[2] = 0x5e; \ (enaddr)[3] = ((const u_char *)ipaddr)[1] & 0x7f; \ (enaddr)[4] = ((const u_char *)ipaddr)[2]; \ (enaddr)[5] = ((const u_char *)ipaddr)[3]; \ } /* * Macro to map an IP6 multicast address to an Ethernet multicast address. * The high-order 16 bits of the Ethernet address are statically assigned, * and the low-order 32 bits are taken from the low end of the IP6 address. */ #define ETHER_MAP_IPV6_MULTICAST(ip6addr, enaddr) \ /* struct in6_addr *ip6addr; */ \ /* u_char enaddr[ETHER_ADDR_LEN]; */ \ { \ (enaddr)[0] = 0x33; \ (enaddr)[1] = 0x33; \ (enaddr)[2] = ((const u_char *)ip6addr)[12]; \ (enaddr)[3] = ((const u_char *)ip6addr)[13]; \ (enaddr)[4] = ((const u_char *)ip6addr)[14]; \ (enaddr)[5] = ((const u_char *)ip6addr)[15]; \ } /* * Ethernet Address Resolution Protocol. * * See RFC 826 for protocol description. Structure below is adapted * to resolving internet addresses. Field names used correspond to * RFC 826. */ struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ u_char arp_spa[4]; /* sender protocol address */ u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ u_char arp_tpa[4]; /* target protocol address */ }; #define arp_hrd ea_hdr.ar_hrd #define arp_pro ea_hdr.ar_pro #define arp_hln ea_hdr.ar_hln #define arp_pln ea_hdr.ar_pln #define arp_op ea_hdr.ar_op #ifndef BURN_BRIDGES /* Can be used by third party software. */ struct sockaddr_inarp { u_char sin_len; u_char sin_family; u_short sin_port; struct in_addr sin_addr; struct in_addr sin_srcaddr; u_short sin_tos; u_short sin_other; #define SIN_PROXY 1 }; #endif /* !BURN_BRIDGES */ /* * IP and ethernet specific routing flags */ #define RTF_USETRAILERS RTF_PROTO1 /* use trailers */ #define RTF_ANNOUNCE RTF_PROTO2 /* announce new arp entry */ #ifdef _KERNEL extern u_char ether_ipmulticast_min[ETHER_ADDR_LEN]; extern u_char ether_ipmulticast_max[ETHER_ADDR_LEN]; struct ifaddr; +int arpresolve_addr(struct ifnet *ifp, int flags, + const struct sockaddr *dst, char *desten, uint32_t *pflags); int arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, const struct sockaddr *dst, u_char *desten, uint32_t *pflags); void arprequest(struct ifnet *, const struct in_addr *, const struct in_addr *, u_char *); void arp_ifinit(struct ifnet *, struct ifaddr *); void arp_announce_ifaddr(struct ifnet *, struct in_addr addr, u_char *); #endif #endif Index: projects/clang380-import/sys/netinet/in.c =================================================================== --- projects/clang380-import/sys/netinet/in.c (revision 293015) +++ projects/clang380-import/sys/netinet/in.c (revision 293016) @@ -1,1414 +1,1422 @@ /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * Copyright (C) 2001 WIDE 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.c 8.4 (Berkeley) 1/9/95 */ #include __FBSDID("$FreeBSD$"); #include "opt_mpath.h" #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 #include #include #include #include #include static int in_aifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); static int in_difaddr_ioctl(caddr_t, struct ifnet *, struct thread *); static void in_socktrim(struct sockaddr_in *); static void in_purgemaddrs(struct ifnet *); static VNET_DEFINE(int, nosameprefix); #define V_nosameprefix VNET(nosameprefix) SYSCTL_INT(_net_inet_ip, OID_AUTO, no_same_prefix, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nosameprefix), 0, "Refuse to create same prefixes on different interfaces"); VNET_DECLARE(struct inpcbinfo, ripcbinfo); #define V_ripcbinfo VNET(ripcbinfo) static struct sx in_control_sx; SX_SYSINIT(in_control_sx, &in_control_sx, "in_control"); /* * Return 1 if an internet address is for a ``local'' host * (one to which we have a connection). */ int in_localaddr(struct in_addr in) { struct rm_priotracker in_ifa_tracker; register u_long i = ntohl(in.s_addr); register struct in_ifaddr *ia; IN_IFADDR_RLOCK(&in_ifa_tracker); TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { if ((i & ia->ia_subnetmask) == ia->ia_subnet) { IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (1); } } IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (0); } /* * Return 1 if an internet address is for the local host and configured * on one of its interfaces. */ int in_localip(struct in_addr in) { struct rm_priotracker in_ifa_tracker; struct in_ifaddr *ia; IN_IFADDR_RLOCK(&in_ifa_tracker); LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) { if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) { IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (1); } } IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (0); } /* * Return 1 if an internet address is configured on an interface. */ int in_ifhasaddr(struct ifnet *ifp, struct in_addr in) { struct ifaddr *ifa; struct in_ifaddr *ia; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET) continue; ia = (struct in_ifaddr *)ifa; if (ia->ia_addr.sin_addr.s_addr == in.s_addr) { IF_ADDR_RUNLOCK(ifp); return (1); } } IF_ADDR_RUNLOCK(ifp); return (0); } /* * Return a reference to the interface address which is different to * the supplied one but with same IP address value. */ static struct in_ifaddr * in_localip_more(struct in_ifaddr *ia) { struct rm_priotracker in_ifa_tracker; in_addr_t in = IA_SIN(ia)->sin_addr.s_addr; struct in_ifaddr *it; IN_IFADDR_RLOCK(&in_ifa_tracker); LIST_FOREACH(it, INADDR_HASH(in), ia_hash) { if (it != ia && IA_SIN(it)->sin_addr.s_addr == in) { ifa_ref(&it->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (it); } } IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (NULL); } /* * Determine whether an IP address is in a reserved set of addresses * that may not be forwarded, or whether datagrams to that destination * may be forwarded. */ int in_canforward(struct in_addr in) { register u_long i = ntohl(in.s_addr); register u_long net; if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i) || IN_LINKLOCAL(i)) return (0); if (IN_CLASSA(i)) { net = i & IN_CLASSA_NET; if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) return (0); } return (1); } /* * Trim a mask in a sockaddr */ static void in_socktrim(struct sockaddr_in *ap) { register char *cplim = (char *) &ap->sin_addr; register char *cp = (char *) (&ap->sin_addr + 1); ap->sin_len = 0; while (--cp >= cplim) if (*cp) { (ap)->sin_len = cp - (char *) (ap) + 1; break; } } /* * Generic internet control operations (ioctl's). */ int in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { struct ifreq *ifr = (struct ifreq *)data; struct sockaddr_in *addr = (struct sockaddr_in *)&ifr->ifr_addr; struct ifaddr *ifa; struct in_ifaddr *ia; int error; if (ifp == NULL) return (EADDRNOTAVAIL); /* * Filter out 4 ioctls we implement directly. Forward the rest * to specific functions and ifp->if_ioctl(). */ switch (cmd) { case SIOCGIFADDR: case SIOCGIFBRDADDR: case SIOCGIFDSTADDR: case SIOCGIFNETMASK: break; case SIOCDIFADDR: sx_xlock(&in_control_sx); error = in_difaddr_ioctl(data, ifp, td); sx_xunlock(&in_control_sx); return (error); case OSIOCAIFADDR: /* 9.x compat */ case SIOCAIFADDR: sx_xlock(&in_control_sx); error = in_aifaddr_ioctl(cmd, data, ifp, td); sx_xunlock(&in_control_sx); return (error); case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFDSTADDR: case SIOCSIFNETMASK: /* We no longer support that old commands. */ return (EINVAL); default: if (ifp->if_ioctl == NULL) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); } if (addr->sin_addr.s_addr != INADDR_ANY && prison_check_ip4(td->td_ucred, &addr->sin_addr) != 0) return (EADDRNOTAVAIL); /* * Find address for this interface, if it exists. If an * address was specified, find that one instead of the * first one on the interface, if possible. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET) continue; ia = (struct in_ifaddr *)ifa; if (ia->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr) break; } if (ifa == NULL) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_INET) { ia = (struct in_ifaddr *)ifa; if (prison_check_ip4(td->td_ucred, &ia->ia_addr.sin_addr) == 0) break; } if (ifa == NULL) { IF_ADDR_RUNLOCK(ifp); return (EADDRNOTAVAIL); } error = 0; switch (cmd) { case SIOCGIFADDR: *addr = ia->ia_addr; break; case SIOCGIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EINVAL; break; } *addr = ia->ia_broadaddr; break; case SIOCGIFDSTADDR: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { error = EINVAL; break; } *addr = ia->ia_dstaddr; break; case SIOCGIFNETMASK: *addr = ia->ia_sockmask; break; } IF_ADDR_RUNLOCK(ifp); return (error); } static int in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { const struct in_aliasreq *ifra = (struct in_aliasreq *)data; const struct sockaddr_in *addr = &ifra->ifra_addr; const struct sockaddr_in *broadaddr = &ifra->ifra_broadaddr; const struct sockaddr_in *mask = &ifra->ifra_mask; const struct sockaddr_in *dstaddr = &ifra->ifra_dstaddr; const int vhid = (cmd == SIOCAIFADDR) ? ifra->ifra_vhid : 0; struct ifaddr *ifa; struct in_ifaddr *ia; bool iaIsFirst; int error = 0; error = priv_check(td, PRIV_NET_ADDIFADDR); if (error) return (error); /* * ifra_addr must be present and be of INET family. * ifra_broadaddr/ifra_dstaddr and ifra_mask are optional. */ if (addr->sin_len != sizeof(struct sockaddr_in) || addr->sin_family != AF_INET) return (EINVAL); if (broadaddr->sin_len != 0 && (broadaddr->sin_len != sizeof(struct sockaddr_in) || broadaddr->sin_family != AF_INET)) return (EINVAL); if (mask->sin_len != 0 && (mask->sin_len != sizeof(struct sockaddr_in) || mask->sin_family != AF_INET)) return (EINVAL); if ((ifp->if_flags & IFF_POINTOPOINT) && (dstaddr->sin_len != sizeof(struct sockaddr_in) || dstaddr->sin_addr.s_addr == INADDR_ANY)) return (EDESTADDRREQ); if (vhid > 0 && carp_attach_p == NULL) return (EPROTONOSUPPORT); /* * See whether address already exist. */ iaIsFirst = true; ia = NULL; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in_ifaddr *it; if (ifa->ifa_addr->sa_family != AF_INET) continue; it = (struct in_ifaddr *)ifa; iaIsFirst = false; if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr && prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0) ia = it; } IF_ADDR_RUNLOCK(ifp); if (ia != NULL) (void )in_difaddr_ioctl(data, ifp, td); ifa = ifa_alloc(sizeof(struct in_ifaddr), M_WAITOK); ia = (struct in_ifaddr *)ifa; ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; ia->ia_ifp = ifp; ia->ia_addr = *addr; if (mask->sin_len != 0) { ia->ia_sockmask = *mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); } else { in_addr_t i = ntohl(addr->sin_addr.s_addr); /* * Be compatible with network classes, if netmask isn't * supplied, guess it based on classes. */ if (IN_CLASSA(i)) ia->ia_subnetmask = IN_CLASSA_NET; else if (IN_CLASSB(i)) ia->ia_subnetmask = IN_CLASSB_NET; else ia->ia_subnetmask = IN_CLASSC_NET; ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); } ia->ia_subnet = ntohl(addr->sin_addr.s_addr) & ia->ia_subnetmask; in_socktrim(&ia->ia_sockmask); if (ifp->if_flags & IFF_BROADCAST) { if (broadaddr->sin_len != 0) { ia->ia_broadaddr = *broadaddr; } else if (ia->ia_subnetmask == IN_RFC3021_MASK) { ia->ia_broadaddr.sin_addr.s_addr = INADDR_BROADCAST; ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in); ia->ia_broadaddr.sin_family = AF_INET; } else { ia->ia_broadaddr.sin_addr.s_addr = htonl(ia->ia_subnet | ~ia->ia_subnetmask); ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in); ia->ia_broadaddr.sin_family = AF_INET; } } if (ifp->if_flags & IFF_POINTOPOINT) ia->ia_dstaddr = *dstaddr; /* XXXGL: rtinit() needs this strange assignment. */ if (ifp->if_flags & IFF_LOOPBACK) ia->ia_dstaddr = ia->ia_addr; if (vhid != 0) { error = (*carp_attach_p)(&ia->ia_ifa, vhid); if (error) return (error); } /* if_addrhead is already referenced by ifa_alloc() */ IF_ADDR_WLOCK(ifp); TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_ref(ifa); /* in_ifaddrhead */ IN_IFADDR_WLOCK(); TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link); LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); IN_IFADDR_WUNLOCK(); /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ if (ifp->if_ioctl != NULL) { error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); if (error) goto fail1; } /* * Add route for the network. */ if (vhid == 0) { int flags = RTF_UP; if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) flags |= RTF_HOST; error = in_addprefix(ia, flags); if (error) goto fail1; } /* * Add a loopback route to self. */ if (vhid == 0 && (ifp->if_flags & IFF_LOOPBACK) == 0 && ia->ia_addr.sin_addr.s_addr != INADDR_ANY && !((ifp->if_flags & IFF_POINTOPOINT) && ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)) { struct in_ifaddr *eia; eia = in_localip_more(ia); if (eia == NULL) { error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); if (error) goto fail2; } else ifa_free(&eia->ia_ifa); } if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST)) { struct in_addr allhosts_addr; struct in_ifinfo *ii; ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); error = in_joingroup(ifp, &allhosts_addr, NULL, &ii->ii_allhosts); } EVENTHANDLER_INVOKE(ifaddr_event, ifp); return (error); fail2: if (vhid == 0) (void )in_scrubprefix(ia, LLE_STATIC); fail1: if (ia->ia_ifa.ifa_carp) (*carp_detach_p)(&ia->ia_ifa); IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_free(&ia->ia_ifa); /* if_addrhead */ IN_IFADDR_WLOCK(); TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link); LIST_REMOVE(ia, ia_hash); IN_IFADDR_WUNLOCK(); ifa_free(&ia->ia_ifa); /* in_ifaddrhead */ return (error); } static int in_difaddr_ioctl(caddr_t data, struct ifnet *ifp, struct thread *td) { const struct ifreq *ifr = (struct ifreq *)data; const struct sockaddr_in *addr = (const struct sockaddr_in *) &ifr->ifr_addr; struct ifaddr *ifa; struct in_ifaddr *ia; bool deleteAny, iaIsLast; int error; if (td != NULL) { error = priv_check(td, PRIV_NET_DELIFADDR); if (error) return (error); } if (addr->sin_len != sizeof(struct sockaddr_in) || addr->sin_family != AF_INET) deleteAny = true; else deleteAny = false; iaIsLast = true; ia = NULL; IF_ADDR_WLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in_ifaddr *it; if (ifa->ifa_addr->sa_family != AF_INET) continue; it = (struct in_ifaddr *)ifa; if (deleteAny && ia == NULL && (td == NULL || prison_check_ip4(td->td_ucred, &it->ia_addr.sin_addr) == 0)) ia = it; if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr && (td == NULL || prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0)) ia = it; if (it != ia) iaIsLast = false; } if (ia == NULL) { IF_ADDR_WUNLOCK(ifp); return (EADDRNOTAVAIL); } TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_free(&ia->ia_ifa); /* if_addrhead */ IN_IFADDR_WLOCK(); TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link); LIST_REMOVE(ia, ia_hash); IN_IFADDR_WUNLOCK(); /* * in_scrubprefix() kills the interface route. */ in_scrubprefix(ia, LLE_STATIC); /* * in_ifadown gets rid of all the rest of * the routes. This is not quite the right * thing to do, but at least if we are running * a routing process they will come back. */ in_ifadown(&ia->ia_ifa, 1); if (ia->ia_ifa.ifa_carp) (*carp_detach_p)(&ia->ia_ifa); /* * If this is the last IPv4 address configured on this * interface, leave the all-hosts group. * No state-change report need be transmitted. */ if (iaIsLast && (ifp->if_flags & IFF_MULTICAST)) { struct in_ifinfo *ii; ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]); IN_MULTI_LOCK(); if (ii->ii_allhosts) { (void)in_leavegroup_locked(ii->ii_allhosts, NULL); ii->ii_allhosts = NULL; } IN_MULTI_UNLOCK(); } EVENTHANDLER_INVOKE(ifaddr_event, ifp); ifa_free(&ia->ia_ifa); /* in_ifaddrhead */ return (0); } #define rtinitflags(x) \ ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ ? RTF_HOST : 0) /* * Check if we have a route for the given prefix already or add one accordingly. */ int in_addprefix(struct in_ifaddr *target, int flags) { struct rm_priotracker in_ifa_tracker; struct in_ifaddr *ia; struct in_addr prefix, mask, p, m; int error; if ((flags & RTF_HOST) != 0) { prefix = target->ia_dstaddr.sin_addr; mask.s_addr = 0; } else { prefix = target->ia_addr.sin_addr; mask = target->ia_sockmask.sin_addr; prefix.s_addr &= mask.s_addr; } IN_IFADDR_RLOCK(&in_ifa_tracker); /* Look for an existing address with the same prefix, mask, and fib */ TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { if (rtinitflags(ia)) { p = ia->ia_dstaddr.sin_addr; if (prefix.s_addr != p.s_addr) continue; } else { p = ia->ia_addr.sin_addr; m = ia->ia_sockmask.sin_addr; p.s_addr &= m.s_addr; if (prefix.s_addr != p.s_addr || mask.s_addr != m.s_addr) continue; } if (target->ia_ifp->if_fib != ia->ia_ifp->if_fib) continue; /* * If we got a matching prefix route inserted by other * interface address, we are done here. */ if (ia->ia_flags & IFA_ROUTE) { #ifdef RADIX_MPATH if (ia->ia_addr.sin_addr.s_addr == target->ia_addr.sin_addr.s_addr) { IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (EEXIST); } else break; #endif if (V_nosameprefix) { IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (EEXIST); } else { int fibnum; fibnum = V_rt_add_addr_allfibs ? RT_ALL_FIBS : target->ia_ifp->if_fib; rt_addrmsg(RTM_ADD, &target->ia_ifa, fibnum); IN_IFADDR_RUNLOCK(&in_ifa_tracker); return (0); } } } IN_IFADDR_RUNLOCK(&in_ifa_tracker); /* * No-one seem to have this prefix route, so we try to insert it. */ error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); if (!error) target->ia_flags |= IFA_ROUTE; return (error); } /* * Removes either all lle entries for given @ia, or lle * corresponding to @ia address. */ static void in_scrubprefixlle(struct in_ifaddr *ia, int all, u_int flags) { struct sockaddr_in addr, mask; struct sockaddr *saddr, *smask; struct ifnet *ifp; saddr = (struct sockaddr *)&addr; bzero(&addr, sizeof(addr)); addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; smask = (struct sockaddr *)&mask; bzero(&mask, sizeof(mask)); mask.sin_len = sizeof(mask); mask.sin_family = AF_INET; mask.sin_addr.s_addr = ia->ia_subnetmask; ifp = ia->ia_ifp; if (all) { /* * Remove all L2 entries matching given prefix. * Convert address to host representation to avoid * doing this on every callback. ia_subnetmask is already * stored in host representation. */ addr.sin_addr.s_addr = ntohl(ia->ia_addr.sin_addr.s_addr); lltable_prefix_free(AF_INET, saddr, smask, flags); } else { /* Remove interface address only */ addr.sin_addr.s_addr = ia->ia_addr.sin_addr.s_addr; lltable_delete_addr(LLTABLE(ifp), LLE_IFADDR, saddr); } } /* * If there is no other address in the system that can serve a route to the * same prefix, remove the route. Hand over the route to the new address * otherwise. */ int in_scrubprefix(struct in_ifaddr *target, u_int flags) { struct rm_priotracker in_ifa_tracker; struct in_ifaddr *ia; struct in_addr prefix, mask, p, m; int error = 0; /* * Remove the loopback route to the interface address. */ if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) && !(target->ia_ifp->if_flags & IFF_LOOPBACK) && (flags & LLE_STATIC)) { struct in_ifaddr *eia; /* * XXXME: add fib-aware in_localip. * We definitely don't want to switch between * prefixes in different fibs. */ eia = in_localip_more(target); if (eia != NULL) { error = ifa_switch_loopback_route((struct ifaddr *)eia, (struct sockaddr *)&target->ia_addr); ifa_free(&eia->ia_ifa); } else { error = ifa_del_loopback_route((struct ifaddr *)target, (struct sockaddr *)&target->ia_addr); } } if (rtinitflags(target)) { prefix = target->ia_dstaddr.sin_addr; mask.s_addr = 0; } else { prefix = target->ia_addr.sin_addr; mask = target->ia_sockmask.sin_addr; prefix.s_addr &= mask.s_addr; } if ((target->ia_flags & IFA_ROUTE) == 0) { int fibnum; fibnum = V_rt_add_addr_allfibs ? RT_ALL_FIBS : target->ia_ifp->if_fib; rt_addrmsg(RTM_DELETE, &target->ia_ifa, fibnum); /* * Removing address from !IFF_UP interface or * prefix which exists on other interface (along with route). * No entries should exist here except target addr. * Given that, delete this entry only. */ in_scrubprefixlle(target, 0, flags); return (0); } IN_IFADDR_RLOCK(&in_ifa_tracker); TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { if (rtinitflags(ia)) { p = ia->ia_dstaddr.sin_addr; if (prefix.s_addr != p.s_addr) continue; } else { p = ia->ia_addr.sin_addr; m = ia->ia_sockmask.sin_addr; p.s_addr &= m.s_addr; if (prefix.s_addr != p.s_addr || mask.s_addr != m.s_addr) continue; } if ((ia->ia_ifp->if_flags & IFF_UP) == 0) continue; /* * If we got a matching prefix address, move IFA_ROUTE and * the route itself to it. Make sure that routing daemons * get a heads-up. */ if ((ia->ia_flags & IFA_ROUTE) == 0) { ifa_ref(&ia->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); if (error == 0) target->ia_flags &= ~IFA_ROUTE; else log(LOG_INFO, "in_scrubprefix: err=%d, old prefix delete failed\n", error); /* Scrub all entries IFF interface is different */ in_scrubprefixlle(target, target->ia_ifp != ia->ia_ifp, flags); error = rtinit(&ia->ia_ifa, (int)RTM_ADD, rtinitflags(ia) | RTF_UP); if (error == 0) ia->ia_flags |= IFA_ROUTE; else log(LOG_INFO, "in_scrubprefix: err=%d, new prefix add failed\n", error); ifa_free(&ia->ia_ifa); return (error); } } IN_IFADDR_RUNLOCK(&in_ifa_tracker); /* * remove all L2 entries on the given prefix */ in_scrubprefixlle(target, 1, flags); /* * As no-one seem to have this prefix, we can remove the route. */ error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); if (error == 0) target->ia_flags &= ~IFA_ROUTE; else log(LOG_INFO, "in_scrubprefix: err=%d, prefix delete failed\n", error); return (error); } #undef rtinitflags /* * Return 1 if the address might be a local broadcast address. */ int in_broadcast(struct in_addr in, struct ifnet *ifp) { register struct ifaddr *ifa; u_long t; if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY) return (1); if ((ifp->if_flags & IFF_BROADCAST) == 0) return (0); t = ntohl(in.s_addr); /* * Look through the list of addresses for a match * with a broadcast address. */ #define ia ((struct in_ifaddr *)ifa) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_INET && (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || /* * Check for old-style (host 0) broadcast, but * taking into account that RFC 3021 obsoletes it. */ (ia->ia_subnetmask != IN_RFC3021_MASK && t == ia->ia_subnet)) && /* * Check for an all one subnetmask. These * only exist when an interface gets a secondary * address. */ ia->ia_subnetmask != (u_long)0xffffffff) return (1); return (0); #undef ia } /* * On interface removal, clean up IPv4 data structures hung off of the ifnet. */ void in_ifdetach(struct ifnet *ifp) { in_pcbpurgeif0(&V_ripcbinfo, ifp); in_pcbpurgeif0(&V_udbinfo, ifp); in_pcbpurgeif0(&V_ulitecbinfo, ifp); in_purgemaddrs(ifp); } /* * Delete all IPv4 multicast address records, and associated link-layer * multicast address records, associated with ifp. * XXX It looks like domifdetach runs AFTER the link layer cleanup. * XXX This should not race with ifma_protospec being set during * a new allocation, if it does, we have bigger problems. */ static void in_purgemaddrs(struct ifnet *ifp) { LIST_HEAD(,in_multi) purgeinms; struct in_multi *inm, *tinm; struct ifmultiaddr *ifma; LIST_INIT(&purgeinms); IN_MULTI_LOCK(); /* * Extract list of in_multi associated with the detaching ifp * which the PF_INET layer is about to release. * We need to do this as IF_ADDR_LOCK() may be re-acquired * by code further down. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET || ifma->ifma_protospec == NULL) continue; #if 0 KASSERT(ifma->ifma_protospec != NULL, ("%s: ifma_protospec is NULL", __func__)); #endif inm = (struct in_multi *)ifma->ifma_protospec; LIST_INSERT_HEAD(&purgeinms, inm, inm_link); } IF_ADDR_RUNLOCK(ifp); LIST_FOREACH_SAFE(inm, &purgeinms, inm_link, tinm) { LIST_REMOVE(inm, inm_link); inm_release_locked(inm); } igmp_ifdetach(ifp); IN_MULTI_UNLOCK(); } struct in_llentry { struct llentry base; }; #define IN_LLTBL_DEFAULT_HSIZE 32 #define IN_LLTBL_HASH(k, h) \ (((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1)) /* * Do actual deallocation of @lle. * Called by LLE_FREE_LOCKED when number of references * drops to zero. */ static void in_lltable_destroy_lle(struct llentry *lle) { LLE_WUNLOCK(lle); LLE_LOCK_DESTROY(lle); LLE_REQ_DESTROY(lle); free(lle, M_LLTABLE); } static struct llentry * in_lltable_new(struct in_addr addr4, u_int flags) { struct in_llentry *lle; lle = malloc(sizeof(struct in_llentry), M_LLTABLE, M_NOWAIT | M_ZERO); if (lle == NULL) /* NB: caller generates msg */ return NULL; /* * For IPv4 this will trigger "arpresolve" to generate * an ARP request. */ lle->base.la_expire = time_uptime; /* mark expired */ lle->base.r_l3addr.addr4 = addr4; lle->base.lle_refcnt = 1; lle->base.lle_free = in_lltable_destroy_lle; LLE_LOCK_INIT(&lle->base); LLE_REQ_INIT(&lle->base); callout_init(&lle->base.lle_timer, 1); return (&lle->base); } #define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ ((((d).s_addr ^ (a).s_addr) & (m).s_addr)) == 0 ) static int in_lltable_match_prefix(const struct sockaddr *saddr, const struct sockaddr *smask, u_int flags, struct llentry *lle) { struct in_addr addr, mask, lle_addr; addr = ((const struct sockaddr_in *)saddr)->sin_addr; mask = ((const struct sockaddr_in *)smask)->sin_addr; lle_addr.s_addr = ntohl(lle->r_l3addr.addr4.s_addr); if (IN_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) return (0); if (lle->la_flags & LLE_IFADDR) { /* * Delete LLE_IFADDR records IFF address & flag matches. * Note that addr is the interface address within prefix * being matched. * Note also we should handle 'ifdown' cases without removing * ifaddr macs. */ if (addr.s_addr == lle_addr.s_addr && (flags & LLE_STATIC) != 0) return (1); return (0); } /* flags & LLE_STATIC means deleting both dynamic and static entries */ if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) return (1); return (0); } static void in_lltable_free_entry(struct lltable *llt, struct llentry *lle) { struct ifnet *ifp; size_t pkts_dropped; LLE_WLOCK_ASSERT(lle); KASSERT(llt != NULL, ("lltable is NULL")); /* Unlink entry from table if not already */ if ((lle->la_flags & LLE_LINKED) != 0) { ifp = llt->llt_ifp; IF_AFDATA_WLOCK_ASSERT(ifp); lltable_unlink_entry(llt, lle); } /* cancel timer */ if (callout_stop(&lle->lle_timer) > 0) LLE_REMREF(lle); /* Drop hold queue */ pkts_dropped = llentry_free(lle); ARPSTAT_ADD(dropped, pkts_dropped); } static int in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr) { struct rtentry *rt; KASSERT(l3addr->sa_family == AF_INET, ("sin_family %d", l3addr->sa_family)); /* XXX rtalloc1_fib should take a const param */ rt = rtalloc1_fib(__DECONST(struct sockaddr *, l3addr), 0, 0, ifp->if_fib); if (rt == NULL) return (EINVAL); /* * If the gateway for an existing host route matches the target L3 * address, which is a special route inserted by some implementation * such as MANET, and the interface is of the correct type, then * allow for ARP to proceed. */ if (rt->rt_flags & RTF_GATEWAY) { if (!(rt->rt_flags & RTF_HOST) || !rt->rt_ifp || rt->rt_ifp->if_type != IFT_ETHER || (rt->rt_ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) != 0 || memcmp(rt->rt_gateway->sa_data, l3addr->sa_data, sizeof(in_addr_t)) != 0) { RTFREE_LOCKED(rt); return (EINVAL); } } /* * Make sure that at least the destination address is covered * by the route. This is for handling the case where 2 or more * interfaces have the same prefix. An incoming packet arrives * on one interface and the corresponding outgoing packet leaves * another interface. */ if (!(rt->rt_flags & RTF_HOST) && rt->rt_ifp != ifp) { const char *sa, *mask, *addr, *lim; int len; mask = (const char *)rt_mask(rt); /* * Just being extra cautious to avoid some custom * code getting into trouble. */ if (mask == NULL) { RTFREE_LOCKED(rt); return (EINVAL); } sa = (const char *)rt_key(rt); addr = (const char *)l3addr; len = ((const struct sockaddr_in *)l3addr)->sin_len; lim = addr + len; for ( ; addr < lim; sa++, mask++, addr++) { if ((*sa ^ *addr) & *mask) { #ifdef DIAGNOSTIC log(LOG_INFO, "IPv4 address: \"%s\" is not on the network\n", inet_ntoa(((const struct sockaddr_in *)l3addr)->sin_addr)); #endif RTFREE_LOCKED(rt); return (EINVAL); } } } RTFREE_LOCKED(rt); return (0); } static inline uint32_t in_lltable_hash_dst(const struct in_addr dst, uint32_t hsize) { return (IN_LLTBL_HASH(dst.s_addr, hsize)); } static uint32_t in_lltable_hash(const struct llentry *lle, uint32_t hsize) { return (in_lltable_hash_dst(lle->r_l3addr.addr4, hsize)); } static void in_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)sa; bzero(sin, sizeof(*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = lle->r_l3addr.addr4; } static inline struct llentry * in_lltable_find_dst(struct lltable *llt, struct in_addr dst) { struct llentry *lle; struct llentries *lleh; u_int hashidx; hashidx = in_lltable_hash_dst(dst, llt->llt_hsize); lleh = &llt->lle_head[hashidx]; LIST_FOREACH(lle, lleh, lle_next) { if (lle->la_flags & LLE_DELETED) continue; if (lle->r_l3addr.addr4.s_addr == dst.s_addr) break; } return (lle); } static void in_lltable_delete_entry(struct lltable *llt, struct llentry *lle) { lle->la_flags |= LLE_DELETED; EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); #ifdef DIAGNOSTIC log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif llentry_free(lle); } static struct llentry * in_lltable_alloc(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr; struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; + char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; KASSERT(l3addr->sa_family == AF_INET, ("sin_family %d", l3addr->sa_family)); /* * A route that covers the given address must have * been installed 1st because we are doing a resolution, * verify this. */ if (!(flags & LLE_IFADDR) && in_lltable_rtcheck(ifp, flags, l3addr) != 0) return (NULL); lle = in_lltable_new(sin->sin_addr, flags); if (lle == NULL) { log(LOG_INFO, "lla_lookup: new lle malloc failed\n"); return (NULL); } lle->la_flags = flags; if (flags & LLE_STATIC) lle->r_flags |= RLLE_VALID; if ((flags & LLE_IFADDR) == LLE_IFADDR) { - lltable_set_entry_addr(ifp, lle, IF_LLADDR(ifp)); + linkhdrsize = LLE_MAX_LINKHDR; + if (lltable_calc_llheader(ifp, AF_INET, IF_LLADDR(ifp), + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return (NULL); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, + lladdr_off); lle->la_flags |= LLE_STATIC; lle->r_flags |= (RLLE_VALID | RLLE_IFADDR); } return (lle); } /* * Return NULL if not found or marked for deletion. * If found return lle read locked. */ static struct llentry * in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr; struct llentry *lle; IF_AFDATA_LOCK_ASSERT(llt->llt_ifp); KASSERT(l3addr->sa_family == AF_INET, ("sin_family %d", l3addr->sa_family)); lle = in_lltable_find_dst(llt, sin->sin_addr); if (lle == NULL) return (NULL); KASSERT((flags & (LLE_UNLOCKED|LLE_EXCLUSIVE)) != (LLE_UNLOCKED|LLE_EXCLUSIVE),("wrong lle request flags: 0x%X", flags)); if (flags & LLE_UNLOCKED) return (lle); if (flags & LLE_EXCLUSIVE) LLE_WLOCK(lle); else LLE_RLOCK(lle); return (lle); } static int in_lltable_dump_entry(struct lltable *llt, struct llentry *lle, struct sysctl_req *wr) { struct ifnet *ifp = llt->llt_ifp; /* XXX stack use */ struct { struct rt_msghdr rtm; struct sockaddr_in sin; struct sockaddr_dl sdl; } arpc; struct sockaddr_dl *sdl; int error; bzero(&arpc, sizeof(arpc)); /* skip deleted entries */ if ((lle->la_flags & LLE_DELETED) == LLE_DELETED) return (0); /* Skip if jailed and not a valid IP of the prison. */ lltable_fill_sa_entry(lle,(struct sockaddr *)&arpc.sin); if (prison_if(wr->td->td_ucred, (struct sockaddr *)&arpc.sin) != 0) return (0); /* * produce a msg made of: * struct rt_msghdr; * struct sockaddr_in; (IPv4) * struct sockaddr_dl; */ arpc.rtm.rtm_msglen = sizeof(arpc); arpc.rtm.rtm_version = RTM_VERSION; arpc.rtm.rtm_type = RTM_GET; arpc.rtm.rtm_flags = RTF_UP; arpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; /* publish */ if (lle->la_flags & LLE_PUB) arpc.rtm.rtm_flags |= RTF_ANNOUNCE; sdl = &arpc.sdl; sdl->sdl_family = AF_LINK; sdl->sdl_len = sizeof(*sdl); sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; if ((lle->la_flags & LLE_VALID) == LLE_VALID) { sdl->sdl_alen = ifp->if_addrlen; - bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); + bcopy(lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); } else { sdl->sdl_alen = 0; bzero(LLADDR(sdl), ifp->if_addrlen); } arpc.rtm.rtm_rmx.rmx_expire = lle->la_flags & LLE_STATIC ? 0 : lle->la_expire; arpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA); if (lle->la_flags & LLE_STATIC) arpc.rtm.rtm_flags |= RTF_STATIC; if (lle->la_flags & LLE_IFADDR) arpc.rtm.rtm_flags |= RTF_PINNED; arpc.rtm.rtm_index = ifp->if_index; error = SYSCTL_OUT(wr, &arpc, sizeof(arpc)); return (error); } static struct lltable * in_lltattach(struct ifnet *ifp) { struct lltable *llt; llt = lltable_allocate_htbl(IN_LLTBL_DEFAULT_HSIZE); llt->llt_af = AF_INET; llt->llt_ifp = ifp; llt->llt_lookup = in_lltable_lookup; llt->llt_alloc_entry = in_lltable_alloc; llt->llt_delete_entry = in_lltable_delete_entry; llt->llt_dump_entry = in_lltable_dump_entry; llt->llt_hash = in_lltable_hash; llt->llt_fill_sa_entry = in_lltable_fill_sa_entry; llt->llt_free_entry = in_lltable_free_entry; llt->llt_match_prefix = in_lltable_match_prefix; lltable_link(llt); return (llt); } void * in_domifattach(struct ifnet *ifp) { struct in_ifinfo *ii; ii = malloc(sizeof(struct in_ifinfo), M_IFADDR, M_WAITOK|M_ZERO); ii->ii_llt = in_lltattach(ifp); ii->ii_igmp = igmp_domifattach(ifp); return (ii); } void in_domifdetach(struct ifnet *ifp, void *aux) { struct in_ifinfo *ii = (struct in_ifinfo *)aux; igmp_domifdetach(ifp); lltable_free(ii->ii_llt); free(ii, M_IFADDR); } Index: projects/clang380-import/sys/netinet/ip_output.c =================================================================== --- projects/clang380-import/sys/netinet/ip_output.c (revision 293015) +++ projects/clang380-import/sys/netinet/ip_output.c (revision 293016) @@ -1,1391 +1,1391 @@ /*- * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_ipfw.h" #include "opt_ipsec.h" #include "opt_mbuf_stress_test.h" #include "opt_mpath.h" #include "opt_route.h" #include "opt_sctp.h" #include "opt_rss.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RADIX_MPATH #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef SCTP #include #include #endif #ifdef IPSEC #include #include #endif /* IPSEC*/ #include #include #ifdef MBUF_STRESS_TEST static int mbuf_frag_size = 0; SYSCTL_INT(_net_inet_ip, OID_AUTO, mbuf_frag_size, CTLFLAG_RW, &mbuf_frag_size, 0, "Fragment outgoing mbufs to this size"); #endif static void ip_mloopback(struct ifnet *, const struct mbuf *, int); extern int in_mcast_loop; extern struct protosw inetsw[]; static inline int ip_output_pfil(struct mbuf **mp, struct ifnet *ifp, struct inpcb *inp, struct sockaddr_in *dst, int *fibnum, int *error) { struct m_tag *fwd_tag = NULL; struct mbuf *m; struct in_addr odst; struct ip *ip; m = *mp; ip = mtod(m, struct ip *); /* Run through list of hooks for output packets. */ odst.s_addr = ip->ip_dst.s_addr; *error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp, PFIL_OUT, inp); m = *mp; if ((*error) != 0 || m == NULL) return 1; /* Finished */ ip = mtod(m, struct ip *); /* See if destination IP address was changed by packet filter. */ if (odst.s_addr != ip->ip_dst.s_addr) { m->m_flags |= M_SKIP_FIREWALL; /* If destination is now ourself drop to ip_input(). */ if (in_localip(ip->ip_dst)) { m->m_flags |= M_FASTFWD_OURS; if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif *error = netisr_queue(NETISR_IP, m); return 1; /* Finished */ } bzero(dst, sizeof(*dst)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; return -1; /* Reloop */ } /* See if fib was changed by packet filter. */ if ((*fibnum) != M_GETFIB(m)) { m->m_flags |= M_SKIP_FIREWALL; *fibnum = M_GETFIB(m); return -1; /* Reloop for FIB change */ } /* See if local, if yes, send it to netisr with IP_FASTFWD_OURS. */ if (m->m_flags & M_FASTFWD_OURS) { if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; *error = netisr_queue(NETISR_IP, m); return 1; /* Finished */ } /* Or forward to some other address? */ if ((m->m_flags & M_IP_NEXTHOP) && ((fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL)) { bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in)); m->m_flags |= M_SKIP_FIREWALL; m->m_flags &= ~M_IP_NEXTHOP; m_tag_delete(m, fwd_tag); return -1; /* Reloop for CHANGE of dst */ } return 0; } /* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. * If route ro is present and has ro_rt initialized, route lookup would be * skipped and ro->ro_rt would be used. If ro is present but ro->ro_rt is NULL, * then result of route lookup is stored in ro->ro_rt. * * In the IP forwarding case, the packet will arrive with options already * inserted, so must have a NULL opt pointer. */ int ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags, struct ip_moptions *imo, struct inpcb *inp) { struct rm_priotracker in_ifa_tracker; struct ip *ip; struct ifnet *ifp = NULL; /* keep compiler happy */ struct mbuf *m0; int hlen = sizeof (struct ip); int mtu; int error = 0; struct sockaddr_in *dst; const struct sockaddr_in *gw; struct in_ifaddr *ia; int isbroadcast; uint16_t ip_len, ip_off; struct route iproute; struct rtentry *rte; /* cache for ro->ro_rt */ uint32_t fibnum; int have_ia_ref; #ifdef IPSEC int no_route_but_check_spd = 0; #endif M_ASSERTPKTHDR(m); if (inp != NULL) { INP_LOCK_ASSERT(inp); M_SETFIB(m, inp->inp_inc.inc_fibnum); if ((flags & IP_NODEFAULTFLOWID) == 0) { m->m_pkthdr.flowid = inp->inp_flowid; M_HASHTYPE_SET(m, inp->inp_flowtype); } } if (ro == NULL) { ro = &iproute; bzero(ro, sizeof (*ro)); } #ifdef FLOWTABLE if (ro->ro_rt == NULL) (void )flowtable_lookup(AF_INET, m, ro); #endif if (opt) { int len = 0; m = ip_insertoptions(m, opt, &len); if (len != 0) hlen = len; /* ip->ip_hl is updated above */ } ip = mtod(m, struct ip *); ip_len = ntohs(ip->ip_len); ip_off = ntohs(ip->ip_off); if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_v = IPVERSION; ip->ip_hl = hlen >> 2; ip_fillid(ip); IPSTAT_INC(ips_localout); } else { /* Header already set, fetch hlen from there */ hlen = ip->ip_hl << 2; } /* * dst/gw handling: * * dst can be rewritten but always points to &ro->ro_dst. * gw is readonly but can point either to dst OR rt_gateway, * therefore we need restore gw if we're redoing lookup. */ gw = dst = (struct sockaddr_in *)&ro->ro_dst; fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); rte = ro->ro_rt; /* * The address family should also be checked in case of sharing * the cache with IPv6. */ if (rte == NULL || dst->sin_family != AF_INET) { bzero(dst, sizeof(*dst)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; } again: ia = NULL; have_ia_ref = 0; /* * If routing to interface only, short circuit routing lookup. * The use of an all-ones broadcast address implies this; an * interface is specified by the broadcast address of an interface, * or the destination address of a ptp interface. */ if (flags & IP_SENDONES) { if ((ia = ifatoia(ifa_ifwithbroadaddr(sintosa(dst), M_GETFIB(m)))) == NULL && (ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst), M_GETFIB(m)))) == NULL) { IPSTAT_INC(ips_noroute); error = ENETUNREACH; goto bad; } have_ia_ref = 1; ip->ip_dst.s_addr = INADDR_BROADCAST; dst->sin_addr = ip->ip_dst; ifp = ia->ia_ifp; ip->ip_ttl = 1; isbroadcast = 1; } else if (flags & IP_ROUTETOIF) { if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst), M_GETFIB(m)))) == NULL && (ia = ifatoia(ifa_ifwithnet(sintosa(dst), 0, M_GETFIB(m)))) == NULL) { IPSTAT_INC(ips_noroute); error = ENETUNREACH; goto bad; } have_ia_ref = 1; ifp = ia->ia_ifp; ip->ip_ttl = 1; isbroadcast = in_broadcast(dst->sin_addr, ifp); } else if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) && imo != NULL && imo->imo_multicast_ifp != NULL) { /* * Bypass the normal routing lookup for multicast * packets if the interface is specified. */ ifp = imo->imo_multicast_ifp; IFP_TO_IA(ifp, ia, &in_ifa_tracker); if (ia) have_ia_ref = 1; isbroadcast = 0; /* fool gcc */ } else { /* * We want to do any cloning requested by the link layer, * as this is probably required in all cases for correct * operation (as it is for ARP). */ if (rte == NULL) { #ifdef RADIX_MPATH rtalloc_mpath_fib(ro, ntohl(ip->ip_src.s_addr ^ ip->ip_dst.s_addr), fibnum); #else in_rtalloc_ign(ro, 0, fibnum); #endif rte = ro->ro_rt; } if (rte == NULL || (rte->rt_flags & RTF_UP) == 0 || rte->rt_ifp == NULL || !RT_LINK_IS_UP(rte->rt_ifp)) { #ifdef IPSEC /* * There is no route for this packet, but it is * possible that a matching SPD entry exists. */ no_route_but_check_spd = 1; mtu = 0; /* Silence GCC warning. */ goto sendit; #endif IPSTAT_INC(ips_noroute); error = EHOSTUNREACH; goto bad; } ia = ifatoia(rte->rt_ifa); ifp = rte->rt_ifp; counter_u64_add(rte->rt_pksent, 1); if (rte->rt_flags & RTF_GATEWAY) gw = (struct sockaddr_in *)rte->rt_gateway; if (rte->rt_flags & RTF_HOST) isbroadcast = (rte->rt_flags & RTF_BROADCAST); else isbroadcast = in_broadcast(gw->sin_addr, ifp); } /* * Calculate MTU. If we have a route that is up, use that, * otherwise use the interface's MTU. */ if (rte != NULL && (rte->rt_flags & (RTF_UP|RTF_HOST))) mtu = rte->rt_mtu; else mtu = ifp->if_mtu; /* Catch a possible divide by zero later. */ KASSERT(mtu > 0, ("%s: mtu %d <= 0, rte=%p (rt_flags=0x%08x) ifp=%p", __func__, mtu, rte, (rte != NULL) ? rte->rt_flags : 0, ifp)); if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { m->m_flags |= M_MCAST; /* * IP destination address is multicast. Make sure "gw" * still points to the address in "ro". (It may have been * changed to point to a gateway address, above.) */ gw = dst; /* * See if the caller provided any multicast options */ if (imo != NULL) { ip->ip_ttl = imo->imo_multicast_ttl; if (imo->imo_multicast_vif != -1) ip->ip_src.s_addr = ip_mcast_src ? ip_mcast_src(imo->imo_multicast_vif) : INADDR_ANY; } else ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; /* * Confirm that the outgoing interface supports multicast. */ if ((imo == NULL) || (imo->imo_multicast_vif == -1)) { if ((ifp->if_flags & IFF_MULTICAST) == 0) { IPSTAT_INC(ips_noroute); error = ENETUNREACH; goto bad; } } /* * If source address not specified yet, use address * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { /* Interface may have no addresses. */ if (ia != NULL) ip->ip_src = IA_SIN(ia)->sin_addr; } if ((imo == NULL && in_mcast_loop) || (imo && imo->imo_multicast_loop)) { /* * Loop back multicast datagram if not expressly * forbidden to do so, even if we are not a member * of the group; ip_input() will filter it later, * thus deferring a hash lookup and mutex acquisition * at the expense of a cheap copy using m_copym(). */ ip_mloopback(ifp, m, hlen); } else { /* * If we are acting as a multicast router, perform * multicast forwarding as if the packet had just * arrived on the interface to which we are about * to send. The multicast forwarding function * recursively calls this function, using the * IP_FORWARDING flag to prevent infinite recursion. * * Multicasts that are looped back by ip_mloopback(), * above, will be forwarded by the ip_input() routine, * if necessary. */ if (V_ip_mrouter && (flags & IP_FORWARDING) == 0) { /* * If rsvp daemon is not running, do not * set ip_moptions. This ensures that the packet * is multicast and not just sent down one link * as prescribed by rsvpd. */ if (!V_rsvp_on) imo = NULL; if (ip_mforward && ip_mforward(ip, ifp, m, imo) != 0) { m_freem(m); goto done; } } } /* * Multicasts with a time-to-live of zero may be looped- * back, above, but must not be transmitted on a network. * Also, multicasts addressed to the loopback interface * are not sent -- the above call to ip_mloopback() will * loop back a copy. ip_input() will drop the copy if * this host does not belong to the destination group on * the loopback interface. */ if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) { m_freem(m); goto done; } goto sendit; } /* * If the source address is not specified yet, use the address * of the outoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { /* Interface may have no addresses. */ if (ia != NULL) { ip->ip_src = IA_SIN(ia)->sin_addr; } } /* * Look for broadcast address and * verify user is allowed to send * such a packet. */ if (isbroadcast) { if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EADDRNOTAVAIL; goto bad; } if ((flags & IP_ALLOWBROADCAST) == 0) { error = EACCES; goto bad; } /* don't allow broadcast messages to be fragmented */ if (ip_len > mtu) { error = EMSGSIZE; goto bad; } m->m_flags |= M_BCAST; } else { m->m_flags &= ~M_BCAST; } sendit: #ifdef IPSEC switch(ip_ipsec_output(&m, inp, &error)) { case 1: goto bad; case -1: goto done; case 0: default: break; /* Continue with packet processing. */ } /* * Check if there was a route for this packet; return error if not. */ if (no_route_but_check_spd) { IPSTAT_INC(ips_noroute); error = EHOSTUNREACH; goto bad; } /* Update variables that are affected by ipsec4_output(). */ ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; #endif /* IPSEC */ /* Jump over all PFIL processing if hooks are not active. */ if (PFIL_HOOKED(&V_inet_pfil_hook)) { switch (ip_output_pfil(&m, ifp, inp, dst, &fibnum, &error)) { case 1: /* Finished */ goto done; case 0: /* Continue normally */ ip = mtod(m, struct ip *); break; case -1: /* Need to try again */ /* Reset everything for a new round */ RO_RTFREE(ro); if (have_ia_ref) ifa_free(&ia->ia_ifa); - ro->ro_lle = NULL; + ro->ro_prepend = NULL; rte = NULL; gw = dst; ip = mtod(m, struct ip *); goto again; } } /* 127/8 must not appear on wire - RFC1122. */ if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { if ((ifp->if_flags & IFF_LOOPBACK) == 0) { IPSTAT_INC(ips_badaddr); error = EADDRNOTAVAIL; goto bad; } } m->m_pkthdr.csum_flags |= CSUM_IP; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA & ~ifp->if_hwassist) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP & ~ifp->if_hwassist) { sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2)); m->m_pkthdr.csum_flags &= ~CSUM_SCTP; } #endif /* * If small enough for interface, or the interface will take * care of the fragmentation for us, we can just send directly. */ if (ip_len <= mtu || (m->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0) { ip->ip_sum = 0; if (m->m_pkthdr.csum_flags & CSUM_IP & ~ifp->if_hwassist) { ip->ip_sum = in_cksum(m, hlen); m->m_pkthdr.csum_flags &= ~CSUM_IP; } /* * Record statistics for this interface address. * With CSUM_TSO the byte/packet count will be slightly * incorrect because we count the IP+TCP headers only * once instead of for every generated packet. */ if (!(flags & IP_FORWARDING) && ia) { if (m->m_pkthdr.csum_flags & CSUM_TSO) counter_u64_add(ia->ia_ifa.ifa_opackets, m->m_pkthdr.len / m->m_pkthdr.tso_segsz); else counter_u64_add(ia->ia_ifa.ifa_opackets, 1); counter_u64_add(ia->ia_ifa.ifa_obytes, m->m_pkthdr.len); } #ifdef MBUF_STRESS_TEST if (mbuf_frag_size && m->m_pkthdr.len > mbuf_frag_size) m = m_fragment(m, M_NOWAIT, mbuf_frag_size); #endif /* * Reset layer specific mbuf flags * to avoid confusing lower layers. */ m_clrprotoflags(m); IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (const struct sockaddr *)gw, ro); goto done; } /* Balk when DF bit is set or the interface didn't support TSO. */ if ((ip_off & IP_DF) || (m->m_pkthdr.csum_flags & CSUM_TSO)) { error = EMSGSIZE; IPSTAT_INC(ips_cantfrag); goto bad; } /* * Too large for interface; fragment if possible. If successful, * on return, m will point to a list of packets to be sent. */ error = ip_fragment(ip, &m, mtu, ifp->if_hwassist); if (error) goto bad; for (; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { /* Record statistics for this interface address. */ if (ia != NULL) { counter_u64_add(ia->ia_ifa.ifa_opackets, 1); counter_u64_add(ia->ia_ifa.ifa_obytes, m->m_pkthdr.len); } /* * Reset layer specific mbuf flags * to avoid confusing upper layers. */ m_clrprotoflags(m); IP_PROBE(send, NULL, NULL, ip, ifp, ip, NULL); error = (*ifp->if_output)(ifp, m, (const struct sockaddr *)gw, ro); } else m_freem(m); } if (error == 0) IPSTAT_INC(ips_fragmented); done: if (ro == &iproute) RO_RTFREE(ro); else if (rte == NULL) /* * If the caller supplied a route but somehow the reference * to it has been released need to prevent the caller * calling RTFREE on it again. */ ro->ro_rt = NULL; if (have_ia_ref) ifa_free(&ia->ia_ifa); return (error); bad: m_freem(m); goto done; } /* * Create a chain of fragments which fit the given mtu. m_frag points to the * mbuf to be fragmented; on return it points to the chain with the fragments. * Return 0 if no error. If error, m_frag may contain a partially built * chain of fragments that should be freed by the caller. * * if_hwassist_flags is the hw offload capabilities (see if_data.ifi_hwassist) */ int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, u_long if_hwassist_flags) { int error = 0; int hlen = ip->ip_hl << 2; int len = (mtu - hlen) & ~7; /* size of payload in each fragment */ int off; struct mbuf *m0 = *m_frag; /* the original packet */ int firstlen; struct mbuf **mnext; int nfrags; uint16_t ip_len, ip_off; ip_len = ntohs(ip->ip_len); ip_off = ntohs(ip->ip_off); if (ip_off & IP_DF) { /* Fragmentation not allowed */ IPSTAT_INC(ips_cantfrag); return EMSGSIZE; } /* * Must be able to put at least 8 bytes per fragment. */ if (len < 8) return EMSGSIZE; /* * If the interface will not calculate checksums on * fragmented packets, then do it here. */ if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m0); m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } #ifdef SCTP if (m0->m_pkthdr.csum_flags & CSUM_SCTP) { sctp_delayed_cksum(m0, hlen); m0->m_pkthdr.csum_flags &= ~CSUM_SCTP; } #endif if (len > PAGE_SIZE) { /* * Fragment large datagrams such that each segment * contains a multiple of PAGE_SIZE amount of data, * plus headers. This enables a receiver to perform * page-flipping zero-copy optimizations. * * XXX When does this help given that sender and receiver * could have different page sizes, and also mtu could * be less than the receiver's page size ? */ int newlen; off = MIN(mtu, m0->m_pkthdr.len); /* * firstlen (off - hlen) must be aligned on an * 8-byte boundary */ if (off < hlen) goto smart_frag_failure; off = ((off - hlen) & ~7) + hlen; newlen = (~PAGE_MASK) & mtu; if ((newlen + sizeof (struct ip)) > mtu) { /* we failed, go back the default */ smart_frag_failure: newlen = len; off = hlen + len; } len = newlen; } else { off = hlen + len; } firstlen = off - hlen; mnext = &m0->m_nextpkt; /* pointer to next packet */ /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. * Here, m0 is the original packet, m is the fragment being created. * The fragments are linked off the m_nextpkt of the original * packet, which after processing serves as the first fragment. */ for (nfrags = 1; off < ip_len; off += len, nfrags++) { struct ip *mhip; /* ip header on the fragment */ struct mbuf *m; int mhlen = sizeof (struct ip); m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; IPSTAT_INC(ips_odropped); goto done; } /* * Make sure the complete packet header gets copied * from the originating mbuf to the newly created * mbuf. This also ensures that existing firewall * classification(s), VLAN tags and so on get copied * to the resulting fragmented packet(s): */ if (m_dup_pkthdr(m, m0, M_NOWAIT) == 0) { m_free(m); error = ENOBUFS; IPSTAT_INC(ips_odropped); goto done; } /* * In the first mbuf, leave room for the link header, then * copy the original IP header including options. The payload * goes into an additional mbuf chain returned by m_copym(). */ m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); *mhip = *ip; if (hlen > sizeof (struct ip)) { mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); mhip->ip_v = IPVERSION; mhip->ip_hl = mhlen >> 2; } m->m_len = mhlen; /* XXX do we need to add ip_off below ? */ mhip->ip_off = ((off - hlen) >> 3) + ip_off; if (off + len >= ip_len) len = ip_len - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); m->m_next = m_copym(m0, off, len, M_NOWAIT); if (m->m_next == NULL) { /* copy failed */ m_free(m); error = ENOBUFS; /* ??? */ IPSTAT_INC(ips_odropped); goto done; } m->m_pkthdr.len = mhlen + len; #ifdef MAC mac_netinet_fragment(m0, m); #endif mhip->ip_off = htons(mhip->ip_off); mhip->ip_sum = 0; if (m->m_pkthdr.csum_flags & CSUM_IP & ~if_hwassist_flags) { mhip->ip_sum = in_cksum(m, mhlen); m->m_pkthdr.csum_flags &= ~CSUM_IP; } *mnext = m; mnext = &m->m_nextpkt; } IPSTAT_ADD(ips_ofragments, nfrags); /* * Update first fragment by trimming what's been copied out * and updating header. */ m_adj(m0, hlen + firstlen - ip_len); m0->m_pkthdr.len = hlen + firstlen; ip->ip_len = htons((u_short)m0->m_pkthdr.len); ip->ip_off = htons(ip_off | IP_MF); ip->ip_sum = 0; if (m0->m_pkthdr.csum_flags & CSUM_IP & ~if_hwassist_flags) { ip->ip_sum = in_cksum(m0, hlen); m0->m_pkthdr.csum_flags &= ~CSUM_IP; } done: *m_frag = m0; return error; } void in_delayed_cksum(struct mbuf *m) { struct ip *ip; uint16_t csum, offset, ip_len; ip = mtod(m, struct ip *); offset = ip->ip_hl << 2 ; ip_len = ntohs(ip->ip_len); csum = in_cksum_skip(m, ip_len, offset); if (m->m_pkthdr.csum_flags & CSUM_UDP && csum == 0) csum = 0xffff; offset += m->m_pkthdr.csum_data; /* checksum offset */ /* find the mbuf in the chain where the checksum starts*/ while ((m != NULL) && (offset >= m->m_len)) { offset -= m->m_len; m = m->m_next; } KASSERT(m != NULL, ("in_delayed_cksum: checksum outside mbuf chain.")); KASSERT(offset + sizeof(u_short) <= m->m_len, ("in_delayed_cksum: checksum split between mbufs.")); *(u_short *)(m->m_data + offset) = csum; } /* * IP socket option processing. */ int ip_ctloutput(struct socket *so, struct sockopt *sopt) { struct inpcb *inp = sotoinpcb(so); int error, optval; #ifdef RSS uint32_t rss_bucket; int retval; #endif error = optval = 0; if (sopt->sopt_level != IPPROTO_IP) { error = EINVAL; if (sopt->sopt_level == SOL_SOCKET && sopt->sopt_dir == SOPT_SET) { switch (sopt->sopt_name) { case SO_REUSEADDR: INP_WLOCK(inp); if ((so->so_options & SO_REUSEADDR) != 0) inp->inp_flags2 |= INP_REUSEADDR; else inp->inp_flags2 &= ~INP_REUSEADDR; INP_WUNLOCK(inp); error = 0; break; case SO_REUSEPORT: INP_WLOCK(inp); if ((so->so_options & SO_REUSEPORT) != 0) inp->inp_flags2 |= INP_REUSEPORT; else inp->inp_flags2 &= ~INP_REUSEPORT; INP_WUNLOCK(inp); error = 0; break; case SO_SETFIB: INP_WLOCK(inp); inp->inp_inc.inc_fibnum = so->so_fibnum; INP_WUNLOCK(inp); error = 0; break; default: break; } } return (error); } switch (sopt->sopt_dir) { case SOPT_SET: switch (sopt->sopt_name) { case IP_OPTIONS: #ifdef notyet case IP_RETOPTS: #endif { struct mbuf *m; if (sopt->sopt_valsize > MLEN) { error = EMSGSIZE; break; } m = m_get(sopt->sopt_td ? M_WAITOK : M_NOWAIT, MT_DATA); if (m == NULL) { error = ENOBUFS; break; } m->m_len = sopt->sopt_valsize; error = sooptcopyin(sopt, mtod(m, char *), m->m_len, m->m_len); if (error) { m_free(m); break; } INP_WLOCK(inp); error = ip_pcbopts(inp, sopt->sopt_name, m); INP_WUNLOCK(inp); return (error); } case IP_BINDANY: if (sopt->sopt_td != NULL) { error = priv_check(sopt->sopt_td, PRIV_NETINET_BINDANY); if (error) break; } /* FALLTHROUGH */ case IP_BINDMULTI: #ifdef RSS case IP_RSS_LISTEN_BUCKET: #endif case IP_TOS: case IP_TTL: case IP_MINTTL: case IP_RECVOPTS: case IP_RECVRETOPTS: case IP_RECVDSTADDR: case IP_RECVTTL: case IP_RECVIF: case IP_ONESBCAST: case IP_DONTFRAG: case IP_RECVTOS: case IP_RECVFLOWID: #ifdef RSS case IP_RECVRSSBUCKETID: #endif error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (sopt->sopt_name) { case IP_TOS: inp->inp_ip_tos = optval; break; case IP_TTL: inp->inp_ip_ttl = optval; break; case IP_MINTTL: if (optval >= 0 && optval <= MAXTTL) inp->inp_ip_minttl = optval; else error = EINVAL; break; #define OPTSET(bit) do { \ INP_WLOCK(inp); \ if (optval) \ inp->inp_flags |= bit; \ else \ inp->inp_flags &= ~bit; \ INP_WUNLOCK(inp); \ } while (0) #define OPTSET2(bit, val) do { \ INP_WLOCK(inp); \ if (val) \ inp->inp_flags2 |= bit; \ else \ inp->inp_flags2 &= ~bit; \ INP_WUNLOCK(inp); \ } while (0) case IP_RECVOPTS: OPTSET(INP_RECVOPTS); break; case IP_RECVRETOPTS: OPTSET(INP_RECVRETOPTS); break; case IP_RECVDSTADDR: OPTSET(INP_RECVDSTADDR); break; case IP_RECVTTL: OPTSET(INP_RECVTTL); break; case IP_RECVIF: OPTSET(INP_RECVIF); break; case IP_ONESBCAST: OPTSET(INP_ONESBCAST); break; case IP_DONTFRAG: OPTSET(INP_DONTFRAG); break; case IP_BINDANY: OPTSET(INP_BINDANY); break; case IP_RECVTOS: OPTSET(INP_RECVTOS); break; case IP_BINDMULTI: OPTSET2(INP_BINDMULTI, optval); break; case IP_RECVFLOWID: OPTSET2(INP_RECVFLOWID, optval); break; #ifdef RSS case IP_RSS_LISTEN_BUCKET: if ((optval >= 0) && (optval < rss_getnumbuckets())) { inp->inp_rss_listen_bucket = optval; OPTSET2(INP_RSS_BUCKET_SET, 1); } else { error = EINVAL; } break; case IP_RECVRSSBUCKETID: OPTSET2(INP_RECVRSSBUCKETID, optval); break; #endif } break; #undef OPTSET #undef OPTSET2 /* * Multicast socket options are processed by the in_mcast * module. */ case IP_MULTICAST_IF: case IP_MULTICAST_VIF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: case IP_ADD_SOURCE_MEMBERSHIP: case IP_DROP_SOURCE_MEMBERSHIP: case IP_BLOCK_SOURCE: case IP_UNBLOCK_SOURCE: case IP_MSFILTER: case MCAST_JOIN_GROUP: case MCAST_LEAVE_GROUP: case MCAST_JOIN_SOURCE_GROUP: case MCAST_LEAVE_SOURCE_GROUP: case MCAST_BLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE: error = inp_setmoptions(inp, sopt); break; case IP_PORTRANGE: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; INP_WLOCK(inp); switch (optval) { case IP_PORTRANGE_DEFAULT: inp->inp_flags &= ~(INP_LOWPORT); inp->inp_flags &= ~(INP_HIGHPORT); break; case IP_PORTRANGE_HIGH: inp->inp_flags &= ~(INP_LOWPORT); inp->inp_flags |= INP_HIGHPORT; break; case IP_PORTRANGE_LOW: inp->inp_flags &= ~(INP_HIGHPORT); inp->inp_flags |= INP_LOWPORT; break; default: error = EINVAL; break; } INP_WUNLOCK(inp); break; #ifdef IPSEC case IP_IPSEC_POLICY: { caddr_t req; struct mbuf *m; if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; req = mtod(m, caddr_t); error = ipsec_set_policy(inp, sopt->sopt_name, req, m->m_len, (sopt->sopt_td != NULL) ? sopt->sopt_td->td_ucred : NULL); m_freem(m); break; } #endif /* IPSEC */ default: error = ENOPROTOOPT; break; } break; case SOPT_GET: switch (sopt->sopt_name) { case IP_OPTIONS: case IP_RETOPTS: if (inp->inp_options) error = sooptcopyout(sopt, mtod(inp->inp_options, char *), inp->inp_options->m_len); else sopt->sopt_valsize = 0; break; case IP_TOS: case IP_TTL: case IP_MINTTL: case IP_RECVOPTS: case IP_RECVRETOPTS: case IP_RECVDSTADDR: case IP_RECVTTL: case IP_RECVIF: case IP_PORTRANGE: case IP_ONESBCAST: case IP_DONTFRAG: case IP_BINDANY: case IP_RECVTOS: case IP_BINDMULTI: case IP_FLOWID: case IP_FLOWTYPE: case IP_RECVFLOWID: #ifdef RSS case IP_RSSBUCKETID: case IP_RECVRSSBUCKETID: #endif switch (sopt->sopt_name) { case IP_TOS: optval = inp->inp_ip_tos; break; case IP_TTL: optval = inp->inp_ip_ttl; break; case IP_MINTTL: optval = inp->inp_ip_minttl; break; #define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0) #define OPTBIT2(bit) (inp->inp_flags2 & bit ? 1 : 0) case IP_RECVOPTS: optval = OPTBIT(INP_RECVOPTS); break; case IP_RECVRETOPTS: optval = OPTBIT(INP_RECVRETOPTS); break; case IP_RECVDSTADDR: optval = OPTBIT(INP_RECVDSTADDR); break; case IP_RECVTTL: optval = OPTBIT(INP_RECVTTL); break; case IP_RECVIF: optval = OPTBIT(INP_RECVIF); break; case IP_PORTRANGE: if (inp->inp_flags & INP_HIGHPORT) optval = IP_PORTRANGE_HIGH; else if (inp->inp_flags & INP_LOWPORT) optval = IP_PORTRANGE_LOW; else optval = 0; break; case IP_ONESBCAST: optval = OPTBIT(INP_ONESBCAST); break; case IP_DONTFRAG: optval = OPTBIT(INP_DONTFRAG); break; case IP_BINDANY: optval = OPTBIT(INP_BINDANY); break; case IP_RECVTOS: optval = OPTBIT(INP_RECVTOS); break; case IP_FLOWID: optval = inp->inp_flowid; break; case IP_FLOWTYPE: optval = inp->inp_flowtype; break; case IP_RECVFLOWID: optval = OPTBIT2(INP_RECVFLOWID); break; #ifdef RSS case IP_RSSBUCKETID: retval = rss_hash2bucket(inp->inp_flowid, inp->inp_flowtype, &rss_bucket); if (retval == 0) optval = rss_bucket; else error = EINVAL; break; case IP_RECVRSSBUCKETID: optval = OPTBIT2(INP_RECVRSSBUCKETID); break; #endif case IP_BINDMULTI: optval = OPTBIT2(INP_BINDMULTI); break; } error = sooptcopyout(sopt, &optval, sizeof optval); break; /* * Multicast socket options are processed by the in_mcast * module. */ case IP_MULTICAST_IF: case IP_MULTICAST_VIF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: case IP_MSFILTER: error = inp_getmoptions(inp, sopt); break; #ifdef IPSEC case IP_IPSEC_POLICY: { struct mbuf *m = NULL; caddr_t req = NULL; size_t len = 0; if (m != 0) { req = mtod(m, caddr_t); len = m->m_len; } error = ipsec_get_policy(sotoinpcb(so), req, len, &m); if (error == 0) error = soopt_mcopyout(sopt, m); /* XXX */ if (error == 0) m_freem(m); break; } #endif /* IPSEC */ default: error = ENOPROTOOPT; break; } break; } return (error); } /* * Routine called from ip_output() to loop back a copy of an IP multicast * packet to the input queue of a specified interface. Note that this * calls the output routine of the loopback "driver", but with an interface * pointer that might NOT be a loopback interface -- evil, but easier than * replicating that code here. */ static void ip_mloopback(struct ifnet *ifp, const struct mbuf *m, int hlen) { struct ip *ip; struct mbuf *copym; /* * Make a deep copy of the packet because we're going to * modify the pack in order to generate checksums. */ copym = m_dup(m, M_NOWAIT); if (copym != NULL && (!M_WRITABLE(copym) || copym->m_len < hlen)) copym = m_pullup(copym, hlen); if (copym != NULL) { /* If needed, compute the checksum and mark it as valid. */ if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(copym); copym->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; copym->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; copym->m_pkthdr.csum_data = 0xffff; } /* * We don't bother to fragment if the IP length is greater * than the interface's MTU. Can this possibly matter? */ ip = mtod(copym, struct ip *); ip->ip_sum = 0; ip->ip_sum = in_cksum(copym, hlen); if_simloop(ifp, copym, AF_INET, 0); } } Index: projects/clang380-import/sys/netinet/toecore.c =================================================================== --- projects/clang380-import/sys/netinet/toecore.c (revision 293015) +++ projects/clang380-import/sys/netinet/toecore.c (revision 293016) @@ -1,584 +1,584 @@ /*- * Copyright (c) 2012 Chelsio Communications, Inc. * All rights reserved. * Written by: Navdeep Parhar * * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TCPSTATES #include #include #include #include #include #include #include static struct mtx toedev_lock; static TAILQ_HEAD(, toedev) toedev_list; static eventhandler_tag listen_start_eh; static eventhandler_tag listen_stop_eh; static eventhandler_tag lle_event_eh; static eventhandler_tag route_redirect_eh; static int toedev_connect(struct toedev *tod __unused, struct socket *so __unused, struct rtentry *rt __unused, struct sockaddr *nam __unused) { return (ENOTSUP); } static int toedev_listen_start(struct toedev *tod __unused, struct tcpcb *tp __unused) { return (ENOTSUP); } static int toedev_listen_stop(struct toedev *tod __unused, struct tcpcb *tp __unused) { return (ENOTSUP); } static void toedev_input(struct toedev *tod __unused, struct tcpcb *tp __unused, struct mbuf *m) { m_freem(m); return; } static void toedev_rcvd(struct toedev *tod __unused, struct tcpcb *tp __unused) { return; } static int toedev_output(struct toedev *tod __unused, struct tcpcb *tp __unused) { return (ENOTSUP); } static void toedev_pcb_detach(struct toedev *tod __unused, struct tcpcb *tp __unused) { return; } static void toedev_l2_update(struct toedev *tod __unused, struct ifnet *ifp __unused, struct sockaddr *sa __unused, uint8_t *lladdr __unused, uint16_t vtag __unused) { return; } static void toedev_route_redirect(struct toedev *tod __unused, struct ifnet *ifp __unused, struct rtentry *rt0 __unused, struct rtentry *rt1 __unused) { return; } static void toedev_syncache_added(struct toedev *tod __unused, void *ctx __unused) { return; } static void toedev_syncache_removed(struct toedev *tod __unused, void *ctx __unused) { return; } static int toedev_syncache_respond(struct toedev *tod __unused, void *ctx __unused, struct mbuf *m) { m_freem(m); return (0); } static void toedev_offload_socket(struct toedev *tod __unused, void *ctx __unused, struct socket *so __unused) { return; } static void toedev_ctloutput(struct toedev *tod __unused, struct tcpcb *tp __unused, int sopt_dir __unused, int sopt_name __unused) { return; } /* * Inform one or more TOE devices about a listening socket. */ static void toe_listen_start(struct inpcb *inp, void *arg) { struct toedev *t, *tod; struct tcpcb *tp; INP_WLOCK_ASSERT(inp); KASSERT(inp->inp_pcbinfo == &V_tcbinfo, ("%s: inp is not a TCP inp", __func__)); if (inp->inp_flags & (INP_DROPPED | INP_TIMEWAIT)) return; tp = intotcpcb(inp); if (tp->t_state != TCPS_LISTEN) return; t = arg; mtx_lock(&toedev_lock); TAILQ_FOREACH(tod, &toedev_list, link) { if (t == NULL || t == tod) tod->tod_listen_start(tod, tp); } mtx_unlock(&toedev_lock); } static void toe_listen_start_event(void *arg __unused, struct tcpcb *tp) { struct inpcb *inp = tp->t_inpcb; INP_WLOCK_ASSERT(inp); KASSERT(tp->t_state == TCPS_LISTEN, ("%s: t_state %s", __func__, tcpstates[tp->t_state])); toe_listen_start(inp, NULL); } static void toe_listen_stop_event(void *arg __unused, struct tcpcb *tp) { struct toedev *tod; #ifdef INVARIANTS struct inpcb *inp = tp->t_inpcb; #endif INP_WLOCK_ASSERT(inp); KASSERT(tp->t_state == TCPS_LISTEN, ("%s: t_state %s", __func__, tcpstates[tp->t_state])); mtx_lock(&toedev_lock); TAILQ_FOREACH(tod, &toedev_list, link) tod->tod_listen_stop(tod, tp); mtx_unlock(&toedev_lock); } /* * Fill up a freshly allocated toedev struct with reasonable defaults. */ void init_toedev(struct toedev *tod) { tod->tod_softc = NULL; /* * Provide no-op defaults so that the kernel can call any toedev * function without having to check whether the TOE driver supplied one * or not. */ tod->tod_connect = toedev_connect; tod->tod_listen_start = toedev_listen_start; tod->tod_listen_stop = toedev_listen_stop; tod->tod_input = toedev_input; tod->tod_rcvd = toedev_rcvd; tod->tod_output = toedev_output; tod->tod_send_rst = toedev_output; tod->tod_send_fin = toedev_output; tod->tod_pcb_detach = toedev_pcb_detach; tod->tod_l2_update = toedev_l2_update; tod->tod_route_redirect = toedev_route_redirect; tod->tod_syncache_added = toedev_syncache_added; tod->tod_syncache_removed = toedev_syncache_removed; tod->tod_syncache_respond = toedev_syncache_respond; tod->tod_offload_socket = toedev_offload_socket; tod->tod_ctloutput = toedev_ctloutput; } /* * Register an active TOE device with the system. This allows it to receive * notifications from the kernel. */ int register_toedev(struct toedev *tod) { struct toedev *t; mtx_lock(&toedev_lock); TAILQ_FOREACH(t, &toedev_list, link) { if (t == tod) { mtx_unlock(&toedev_lock); return (EEXIST); } } TAILQ_INSERT_TAIL(&toedev_list, tod, link); registered_toedevs++; mtx_unlock(&toedev_lock); inp_apply_all(toe_listen_start, tod); return (0); } /* * Remove the TOE device from the global list of active TOE devices. It is the * caller's responsibility to ensure that the TOE device is quiesced prior to * this call. */ int unregister_toedev(struct toedev *tod) { struct toedev *t, *t2; int rc = ENODEV; mtx_lock(&toedev_lock); TAILQ_FOREACH_SAFE(t, &toedev_list, link, t2) { if (t == tod) { TAILQ_REMOVE(&toedev_list, tod, link); registered_toedevs--; rc = 0; break; } } KASSERT(registered_toedevs >= 0, ("%s: registered_toedevs (%d) < 0", __func__, registered_toedevs)); mtx_unlock(&toedev_lock); return (rc); } void toe_syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, struct inpcb *inp, void *tod, void *todctx) { struct socket *lso = inp->inp_socket; INP_WLOCK_ASSERT(inp); syncache_add(inc, to, th, inp, &lso, NULL, tod, todctx); } int toe_syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, struct socket **lsop) { INP_INFO_RLOCK_ASSERT(&V_tcbinfo); return (syncache_expand(inc, to, th, lsop, NULL)); } /* * General purpose check to see if a 4-tuple is in use by the kernel. If a TCP * header (presumably for an incoming SYN) is also provided, an existing 4-tuple * in TIME_WAIT may be assassinated freeing it up for re-use. * * Note that the TCP header must have been run through tcp_fields_to_host() or * equivalent. */ int toe_4tuple_check(struct in_conninfo *inc, struct tcphdr *th, struct ifnet *ifp) { struct inpcb *inp; if (inc->inc_flags & INC_ISIPV6) { inp = in6_pcblookup(&V_tcbinfo, &inc->inc6_faddr, inc->inc_fport, &inc->inc6_laddr, inc->inc_lport, INPLOOKUP_WLOCKPCB, ifp); } else { inp = in_pcblookup(&V_tcbinfo, inc->inc_faddr, inc->inc_fport, inc->inc_laddr, inc->inc_lport, INPLOOKUP_WLOCKPCB, ifp); } if (inp != NULL) { INP_WLOCK_ASSERT(inp); if ((inp->inp_flags & INP_TIMEWAIT) && th != NULL) { INP_INFO_RLOCK_ASSERT(&V_tcbinfo); /* for twcheck */ if (!tcp_twcheck(inp, NULL, th, NULL, 0)) return (EADDRINUSE); } else { INP_WUNLOCK(inp); return (EADDRINUSE); } } return (0); } static void toe_lle_event(void *arg __unused, struct llentry *lle, int evt) { struct toedev *tod; struct ifnet *ifp; struct sockaddr *sa; uint8_t *lladdr; uint16_t vtag; int family; struct sockaddr_in6 sin6; LLE_WLOCK_ASSERT(lle); ifp = lltable_get_ifp(lle->lle_tbl); family = lltable_get_af(lle->lle_tbl); if (family != AF_INET && family != AF_INET6) return; /* * Not interested if the interface's TOE capability is not enabled. */ if ((family == AF_INET && !(ifp->if_capenable & IFCAP_TOE4)) || (family == AF_INET6 && !(ifp->if_capenable & IFCAP_TOE6))) return; tod = TOEDEV(ifp); if (tod == NULL) return; sa = (struct sockaddr *)&sin6; lltable_fill_sa_entry(lle, sa); vtag = 0xfff; if (evt != LLENTRY_RESOLVED) { /* * LLENTRY_TIMEDOUT, LLENTRY_DELETED, LLENTRY_EXPIRED all mean * this entry is going to be deleted. */ lladdr = NULL; } else { KASSERT(lle->la_flags & LLE_VALID, ("%s: %p resolved but not valid?", __func__, lle)); - lladdr = (uint8_t *)&lle->ll_addr; + lladdr = (uint8_t *)lle->ll_addr; #ifdef VLAN_TAG VLAN_TAG(ifp, &vtag); #endif } tod->tod_l2_update(tod, ifp, sa, lladdr, vtag); } /* * XXX: implement. */ static void toe_route_redirect_event(void *arg __unused, struct rtentry *rt0, struct rtentry *rt1, struct sockaddr *sa) { return; } /* * Returns 0 or EWOULDBLOCK on success (any other value is an error). 0 means * lladdr and vtag are valid on return, EWOULDBLOCK means the TOE driver's * tod_l2_update will be called later, when the entry is resolved or times out. */ int toe_l2_resolve(struct toedev *tod, struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr, uint16_t *vtag) { int rc; switch (sa->sa_family) { #ifdef INET case AF_INET: rc = arpresolve(ifp, 0, NULL, sa, lladdr, NULL); break; #endif #ifdef INET6 case AF_INET6: rc = nd6_resolve(ifp, 0, NULL, sa, lladdr, NULL); break; #endif default: return (EPROTONOSUPPORT); } if (rc == 0) { #ifdef VLAN_TAG if (VLAN_TAG(ifp, vtag) != 0) #endif *vtag = 0xfff; } return (rc); } void toe_connect_failed(struct toedev *tod, struct inpcb *inp, int err) { INP_WLOCK_ASSERT(inp); if (!(inp->inp_flags & INP_DROPPED)) { struct tcpcb *tp = intotcpcb(inp); KASSERT(tp->t_flags & TF_TOE, ("%s: tp %p not offloaded.", __func__, tp)); if (err == EAGAIN) { /* * Temporary failure during offload, take this PCB back. * Detach from the TOE driver and do the rest of what * TCP's pru_connect would have done if the connection * wasn't offloaded. */ tod->tod_pcb_detach(tod, tp); KASSERT(!(tp->t_flags & TF_TOE), ("%s: tp %p still offloaded.", __func__, tp)); tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); (void) tp->t_fb->tfb_tcp_output(tp); } else { INP_INFO_RLOCK_ASSERT(&V_tcbinfo); tp = tcp_drop(tp, err); if (tp == NULL) INP_WLOCK(inp); /* re-acquire */ } } INP_WLOCK_ASSERT(inp); } static int toecore_load(void) { mtx_init(&toedev_lock, "toedev lock", NULL, MTX_DEF); TAILQ_INIT(&toedev_list); listen_start_eh = EVENTHANDLER_REGISTER(tcp_offload_listen_start, toe_listen_start_event, NULL, EVENTHANDLER_PRI_ANY); listen_stop_eh = EVENTHANDLER_REGISTER(tcp_offload_listen_stop, toe_listen_stop_event, NULL, EVENTHANDLER_PRI_ANY); lle_event_eh = EVENTHANDLER_REGISTER(lle_event, toe_lle_event, NULL, EVENTHANDLER_PRI_ANY); route_redirect_eh = EVENTHANDLER_REGISTER(route_redirect_event, toe_route_redirect_event, NULL, EVENTHANDLER_PRI_ANY); return (0); } static int toecore_unload(void) { mtx_lock(&toedev_lock); if (!TAILQ_EMPTY(&toedev_list)) { mtx_unlock(&toedev_lock); return (EBUSY); } EVENTHANDLER_DEREGISTER(tcp_offload_listen_start, listen_start_eh); EVENTHANDLER_DEREGISTER(tcp_offload_listen_stop, listen_stop_eh); EVENTHANDLER_DEREGISTER(lle_event, lle_event_eh); EVENTHANDLER_DEREGISTER(route_redirect_event, route_redirect_eh); mtx_unlock(&toedev_lock); mtx_destroy(&toedev_lock); return (0); } static int toecore_mod_handler(module_t mod, int cmd, void *arg) { if (cmd == MOD_LOAD) return (toecore_load()); if (cmd == MOD_UNLOAD) return (toecore_unload()); return (EOPNOTSUPP); } static moduledata_t mod_data= { "toecore", toecore_mod_handler, 0 }; MODULE_VERSION(toecore, 1); DECLARE_MODULE(toecore, mod_data, SI_SUB_EXEC, SI_ORDER_ANY); Index: projects/clang380-import/sys/netinet6/icmp6.c =================================================================== --- projects/clang380-import/sys/netinet6/icmp6.c (revision 293015) +++ projects/clang380-import/sys/netinet6/icmp6.c (revision 293016) @@ -1,2873 +1,2873 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ /*- * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 */ #include __FBSDID("$FreeBSD$"); #define MBUF_PRIVATE /* XXXRW: Optimisation tries to avoid M_EXT mbufs */ #include "opt_inet.h" #include "opt_inet6.h" #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 #include #include #include #include #include #include #include #include #include #include #include extern struct domain inet6domain; VNET_PCPUSTAT_DEFINE(struct icmp6stat, icmp6stat); VNET_PCPUSTAT_SYSINIT(icmp6stat); #ifdef VIMAGE VNET_PCPUSTAT_SYSUNINIT(icmp6stat); #endif /* VIMAGE */ VNET_DECLARE(struct inpcbinfo, ripcbinfo); VNET_DECLARE(struct inpcbhead, ripcb); VNET_DECLARE(int, icmp6errppslim); static VNET_DEFINE(int, icmp6errpps_count) = 0; static VNET_DEFINE(struct timeval, icmp6errppslim_last); VNET_DECLARE(int, icmp6_nodeinfo); #define V_ripcbinfo VNET(ripcbinfo) #define V_ripcb VNET(ripcb) #define V_icmp6errppslim VNET(icmp6errppslim) #define V_icmp6errpps_count VNET(icmp6errpps_count) #define V_icmp6errppslim_last VNET(icmp6errppslim_last) #define V_icmp6_nodeinfo VNET(icmp6_nodeinfo) static void icmp6_errcount(int, int); static int icmp6_rip6_input(struct mbuf **, int); static int icmp6_ratelimit(const struct in6_addr *, const int, const int); static const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *, struct in6_addr *); static struct mbuf *ni6_input(struct mbuf *, int); static struct mbuf *ni6_nametodns(const char *, int, int); static int ni6_dnsmatch(const char *, int, const char *, int); static int ni6_addrs(struct icmp6_nodeinfo *, struct mbuf *, struct ifnet **, struct in6_addr *); static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int); static int icmp6_notify_error(struct mbuf **, int, int, int); /* * Kernel module interface for updating icmp6stat. The argument is an index * into icmp6stat treated as an array of u_quad_t. While this encodes the * general layout of icmp6stat into the caller, it doesn't encode its * location, so that future changes to add, for example, per-CPU stats * support won't cause binary compatibility problems for kernel modules. */ void kmod_icmp6stat_inc(int statnum) { counter_u64_add(VNET(icmp6stat)[statnum], 1); } static void icmp6_errcount(int type, int code) { switch (type) { case ICMP6_DST_UNREACH: switch (code) { case ICMP6_DST_UNREACH_NOROUTE: ICMP6STAT_INC(icp6s_odst_unreach_noroute); return; case ICMP6_DST_UNREACH_ADMIN: ICMP6STAT_INC(icp6s_odst_unreach_admin); return; case ICMP6_DST_UNREACH_BEYONDSCOPE: ICMP6STAT_INC(icp6s_odst_unreach_beyondscope); return; case ICMP6_DST_UNREACH_ADDR: ICMP6STAT_INC(icp6s_odst_unreach_addr); return; case ICMP6_DST_UNREACH_NOPORT: ICMP6STAT_INC(icp6s_odst_unreach_noport); return; } break; case ICMP6_PACKET_TOO_BIG: ICMP6STAT_INC(icp6s_opacket_too_big); return; case ICMP6_TIME_EXCEEDED: switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: ICMP6STAT_INC(icp6s_otime_exceed_transit); return; case ICMP6_TIME_EXCEED_REASSEMBLY: ICMP6STAT_INC(icp6s_otime_exceed_reassembly); return; } break; case ICMP6_PARAM_PROB: switch (code) { case ICMP6_PARAMPROB_HEADER: ICMP6STAT_INC(icp6s_oparamprob_header); return; case ICMP6_PARAMPROB_NEXTHEADER: ICMP6STAT_INC(icp6s_oparamprob_nextheader); return; case ICMP6_PARAMPROB_OPTION: ICMP6STAT_INC(icp6s_oparamprob_option); return; } break; case ND_REDIRECT: ICMP6STAT_INC(icp6s_oredirect); return; } ICMP6STAT_INC(icp6s_ounknown); } /* * A wrapper function for icmp6_error() necessary when the erroneous packet * may not contain enough scope zone information. */ void icmp6_error2(struct mbuf *m, int type, int code, int param, struct ifnet *ifp) { struct ip6_hdr *ip6; if (ifp == NULL) return; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); #else if (m->m_len < sizeof(struct ip6_hdr)) { m = m_pullup(m, sizeof(struct ip6_hdr)); if (m == NULL) return; } #endif ip6 = mtod(m, struct ip6_hdr *); if (in6_setscope(&ip6->ip6_src, ifp, NULL) != 0) return; if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) return; icmp6_error(m, type, code, param); } /* * Generate an error packet of type error in response to bad IP6 packet. */ void icmp6_error(struct mbuf *m, int type, int code, int param) { struct ip6_hdr *oip6, *nip6; struct icmp6_hdr *icmp6; u_int preplen; int off; int nxt; ICMP6STAT_INC(icp6s_error); /* count per-type-code statistics */ icmp6_errcount(type, code); #ifdef M_DECRYPTED /*not openbsd*/ if (m->m_flags & M_DECRYPTED) { ICMP6STAT_INC(icp6s_canterror); goto freeit; } #endif #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); #else if (m->m_len < sizeof(struct ip6_hdr)) { m = m_pullup(m, sizeof(struct ip6_hdr)); if (m == NULL) return; } #endif oip6 = mtod(m, struct ip6_hdr *); /* * If the destination address of the erroneous packet is a multicast * address, or the packet was sent using link-layer multicast, * we should basically suppress sending an error (RFC 2463, Section * 2.4). * We have two exceptions (the item e.2 in that section): * - the Packet Too Big message can be sent for path MTU discovery. * - the Parameter Problem Message that can be allowed an icmp6 error * in the option type field. This check has been done in * ip6_unknown_opt(), so we can just check the type and code. */ if ((m->m_flags & (M_BCAST|M_MCAST) || IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && (type != ICMP6_PACKET_TOO_BIG && (type != ICMP6_PARAM_PROB || code != ICMP6_PARAMPROB_OPTION))) goto freeit; /* * RFC 2463, 2.4 (e.5): source address check. * XXX: the case of anycast source? */ if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) goto freeit; /* * If we are about to send ICMPv6 against ICMPv6 error/redirect, * don't do it. */ nxt = -1; off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); if (off >= 0 && nxt == IPPROTO_ICMPV6) { struct icmp6_hdr *icp; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, sizeof(*icp)); if (icp == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif if (icp->icmp6_type < ICMP6_ECHO_REQUEST || icp->icmp6_type == ND_REDIRECT) { /* * ICMPv6 error * Special case: for redirect (which is * informational) we must not send icmp6 error. */ ICMP6STAT_INC(icp6s_canterror); goto freeit; } else { /* ICMPv6 informational - send the error */ } } else { /* non-ICMPv6 - send the error */ } oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ /* Finally, do rate limitation check. */ if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { ICMP6STAT_INC(icp6s_toofreq); goto freeit; } /* * OK, ICMP6 can be generated. */ if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); M_PREPEND(m, preplen, M_NOWAIT); /* FIB is also copied over. */ if (m == NULL) { nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__)); return; } nip6 = mtod(m, struct ip6_hdr *); nip6->ip6_src = oip6->ip6_src; nip6->ip6_dst = oip6->ip6_dst; in6_clearscope(&oip6->ip6_src); in6_clearscope(&oip6->ip6_dst); icmp6 = (struct icmp6_hdr *)(nip6 + 1); icmp6->icmp6_type = type; icmp6->icmp6_code = code; icmp6->icmp6_pptr = htonl((u_int32_t)param); /* * icmp6_reflect() is designed to be in the input path. * icmp6_error() can be called from both input and output path, * and if we are in output path rcvif could contain bogus value. * clear m->m_pkthdr.rcvif for safety, we should have enough scope * information in ip header (nip6). */ m->m_pkthdr.rcvif = NULL; ICMP6STAT_INC(icp6s_outhist[type]); icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */ return; freeit: /* * If we can't tell whether or not we can generate ICMP6, free it. */ m_freem(m); } /* * Process a received ICMP6 message. */ int icmp6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp, *n; struct ifnet *ifp; struct ip6_hdr *ip6, *nip6; struct icmp6_hdr *icmp6, *nicmp6; int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; int code, sum, noff; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; int ip6len, error; ifp = m->m_pkthdr.rcvif; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); /* m might change if M_LOOP. So, call mtod after this */ #endif /* * Locate icmp6 structure in mbuf, and check * that not corrupted and of at least minimum length */ ip6 = mtod(m, struct ip6_hdr *); ip6len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); if (icmp6len < sizeof(struct icmp6_hdr)) { ICMP6STAT_INC(icp6s_tooshort); goto freeit; } /* * Check multicast group membership. * Note: SSM filters are not applied for ICMPv6 traffic. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct in6_multi *inm; inm = in6m_lookup(ifp, &ip6->ip6_dst); if (inm == NULL) { IP6STAT_INC(ip6s_notmember); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); goto freeit; } } /* * calculate the checksum */ #ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); if (icmp6 == NULL) { ICMP6STAT_INC(icp6s_tooshort); return IPPROTO_DONE; } #endif code = icmp6->icmp6_code; if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { nd6log((LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n", icmp6->icmp6_type, sum, ip6_sprintf(ip6bufs, &ip6->ip6_src))); ICMP6STAT_INC(icp6s_checksum); goto freeit; } ICMP6STAT_INC(icp6s_inhist[icmp6->icmp6_type]); icmp6_ifstat_inc(ifp, ifs6_in_msg); if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) icmp6_ifstat_inc(ifp, ifs6_in_error); switch (icmp6->icmp6_type) { case ICMP6_DST_UNREACH: icmp6_ifstat_inc(ifp, ifs6_in_dstunreach); switch (code) { case ICMP6_DST_UNREACH_NOROUTE: code = PRC_UNREACH_NET; break; case ICMP6_DST_UNREACH_ADMIN: icmp6_ifstat_inc(ifp, ifs6_in_adminprohib); code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ break; case ICMP6_DST_UNREACH_ADDR: code = PRC_HOSTDEAD; break; case ICMP6_DST_UNREACH_BEYONDSCOPE: /* I mean "source address was incorrect." */ code = PRC_PARAMPROB; break; case ICMP6_DST_UNREACH_NOPORT: code = PRC_UNREACH_PORT; break; default: goto badcode; } goto deliver; break; case ICMP6_PACKET_TOO_BIG: icmp6_ifstat_inc(ifp, ifs6_in_pkttoobig); /* validation is made in icmp6_mtudisc_update */ code = PRC_MSGSIZE; /* * Updating the path MTU will be done after examining * intermediate extension headers. */ goto deliver; break; case ICMP6_TIME_EXCEEDED: icmp6_ifstat_inc(ifp, ifs6_in_timeexceed); switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: code = PRC_TIMXCEED_INTRANS; break; case ICMP6_TIME_EXCEED_REASSEMBLY: code = PRC_TIMXCEED_REASS; break; default: goto badcode; } goto deliver; break; case ICMP6_PARAM_PROB: icmp6_ifstat_inc(ifp, ifs6_in_paramprob); switch (code) { case ICMP6_PARAMPROB_NEXTHEADER: code = PRC_UNREACH_PROTOCOL; break; case ICMP6_PARAMPROB_HEADER: case ICMP6_PARAMPROB_OPTION: code = PRC_PARAMPROB; break; default: goto badcode; } goto deliver; break; case ICMP6_ECHO_REQUEST: icmp6_ifstat_inc(ifp, ifs6_in_echo); if (code != 0) goto badcode; if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { /* Give up remote */ break; } if (!M_WRITABLE(n) || n->m_len < off + sizeof(struct icmp6_hdr)) { struct mbuf *n0 = n; int n0len; CTASSERT(sizeof(*nip6) + sizeof(*nicmp6) <= MHLEN); n = m_gethdr(M_NOWAIT, n0->m_type); if (n == NULL) { /* Give up remote */ m_freem(n0); break; } m_move_pkthdr(n, n0); /* FIB copied. */ n0len = n0->m_pkthdr.len; /* save for use below */ /* * Copy IPv6 and ICMPv6 only. */ nip6 = mtod(n, struct ip6_hdr *); bcopy(ip6, nip6, sizeof(struct ip6_hdr)); nicmp6 = (struct icmp6_hdr *)(nip6 + 1); bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); noff = sizeof(struct ip6_hdr); /* new mbuf contains only ipv6+icmpv6 headers */ n->m_len = noff + sizeof(struct icmp6_hdr); /* * Adjust mbuf. ip6_plen will be adjusted in * ip6_output(). */ m_adj(n0, off + sizeof(struct icmp6_hdr)); /* recalculate complete packet size */ n->m_pkthdr.len = n0len + (noff - off); n->m_next = n0; } else { nip6 = mtod(n, struct ip6_hdr *); IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off, sizeof(*nicmp6)); noff = off; } nicmp6->icmp6_type = ICMP6_ECHO_REPLY; nicmp6->icmp6_code = 0; if (n) { ICMP6STAT_INC(icp6s_reflect); ICMP6STAT_INC(icp6s_outhist[ICMP6_ECHO_REPLY]); icmp6_reflect(n, noff); } break; case ICMP6_ECHO_REPLY: icmp6_ifstat_inc(ifp, ifs6_in_echoreply); if (code != 0) goto badcode; break; case MLD_LISTENER_QUERY: case MLD_LISTENER_REPORT: case MLD_LISTENER_DONE: case MLDV2_LISTENER_REPORT: /* * Drop MLD traffic which is not link-local, has a hop limit * of greater than 1 hop, or which does not have the * IPv6 HBH Router Alert option. * As IPv6 HBH options are stripped in ip6_input() we must * check an mbuf header flag. * XXX Should we also sanity check that these messages * were directed to a link-local multicast prefix? */ if ((ip6->ip6_hlim != 1) || (m->m_flags & M_RTALERT_MLD) == 0) goto freeit; if (mld_input(m, off, icmp6len) != 0) return (IPPROTO_DONE); /* m stays. */ break; case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ { enum { WRU, FQDN } mode; if (!V_icmp6_nodeinfo) break; if (icmp6len == sizeof(struct icmp6_hdr) + 4) mode = WRU; else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) mode = FQDN; else goto badlen; if (mode == FQDN) { #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), IPPROTO_DONE); #endif n = m_copy(m, 0, M_COPYALL); if (n) n = ni6_input(n, off); /* XXX meaningless if n == NULL */ noff = sizeof(struct ip6_hdr); } else { struct prison *pr; u_char *p; int maxhlen, hlen; /* * XXX: this combination of flags is pointless, * but should we keep this for compatibility? */ if ((V_icmp6_nodeinfo & 5) != 5) break; if (code != 0) goto badcode; CTASSERT(sizeof(*nip6) + sizeof(*nicmp6) + 4 <= MHLEN); n = m_gethdr(M_NOWAIT, m->m_type); if (n == NULL) { /* Give up remote */ break; } if (!m_dup_pkthdr(n, m, M_NOWAIT)) { /* * Previous code did a blind M_COPY_PKTHDR * and said "just for rcvif". If true, then * we could tolerate the dup failing (due to * the deep copy of the tag chain). For now * be conservative and just fail. */ m_free(n); n = NULL; } maxhlen = M_TRAILINGSPACE(n) - (sizeof(*nip6) + sizeof(*nicmp6) + 4); pr = curthread->td_ucred->cr_prison; mtx_lock(&pr->pr_mtx); hlen = strlen(pr->pr_hostname); if (maxhlen > hlen) maxhlen = hlen; /* * Copy IPv6 and ICMPv6 only. */ nip6 = mtod(n, struct ip6_hdr *); bcopy(ip6, nip6, sizeof(struct ip6_hdr)); nicmp6 = (struct icmp6_hdr *)(nip6 + 1); bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); p = (u_char *)(nicmp6 + 1); bzero(p, 4); /* meaningless TTL */ bcopy(pr->pr_hostname, p + 4, maxhlen); mtx_unlock(&pr->pr_mtx); noff = sizeof(struct ip6_hdr); n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4 + maxhlen; nicmp6->icmp6_type = ICMP6_WRUREPLY; nicmp6->icmp6_code = 0; } if (n) { ICMP6STAT_INC(icp6s_reflect); ICMP6STAT_INC(icp6s_outhist[ICMP6_WRUREPLY]); icmp6_reflect(n, noff); } break; } case ICMP6_WRUREPLY: if (code != 0) goto badcode; break; case ND_ROUTER_SOLICIT: icmp6_ifstat_inc(ifp, ifs6_in_routersolicit); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_router_solicit)) goto badlen; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { /* give up local */ /* Send incoming SeND packet to user space. */ if (send_sendso_input_hook != NULL) { IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); error = send_sendso_input_hook(m, ifp, SND_IN, ip6len); /* -1 == no app on SEND socket */ if (error == 0) return (IPPROTO_DONE); nd6_rs_input(m, off, icmp6len); } else nd6_rs_input(m, off, icmp6len); m = NULL; goto freeit; } if (send_sendso_input_hook != NULL) { IP6_EXTHDR_CHECK(n, off, icmp6len, IPPROTO_DONE); error = send_sendso_input_hook(n, ifp, SND_IN, ip6len); if (error == 0) goto freeit; /* -1 == no app on SEND socket */ nd6_rs_input(n, off, icmp6len); } else nd6_rs_input(n, off, icmp6len); /* m stays. */ break; case ND_ROUTER_ADVERT: icmp6_ifstat_inc(ifp, ifs6_in_routeradvert); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_router_advert)) goto badlen; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { /* Send incoming SeND-protected/ND packet to user space. */ if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(m, ifp, SND_IN, ip6len); if (error == 0) return (IPPROTO_DONE); nd6_ra_input(m, off, icmp6len); } else nd6_ra_input(m, off, icmp6len); m = NULL; goto freeit; } if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(n, ifp, SND_IN, ip6len); if (error == 0) goto freeit; nd6_ra_input(n, off, icmp6len); } else nd6_ra_input(n, off, icmp6len); /* m stays. */ break; case ND_NEIGHBOR_SOLICIT: icmp6_ifstat_inc(ifp, ifs6_in_neighborsolicit); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_solicit)) goto badlen; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(m, ifp, SND_IN, ip6len); if (error == 0) return (IPPROTO_DONE); nd6_ns_input(m, off, icmp6len); } else nd6_ns_input(m, off, icmp6len); m = NULL; goto freeit; } if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(n, ifp, SND_IN, ip6len); if (error == 0) goto freeit; nd6_ns_input(n, off, icmp6len); } else nd6_ns_input(n, off, icmp6len); /* m stays. */ break; case ND_NEIGHBOR_ADVERT: icmp6_ifstat_inc(ifp, ifs6_in_neighboradvert); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_advert)) goto badlen; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { /* Send incoming SeND-protected/ND packet to user space. */ if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(m, ifp, SND_IN, ip6len); if (error == 0) return (IPPROTO_DONE); nd6_na_input(m, off, icmp6len); } else nd6_na_input(m, off, icmp6len); m = NULL; goto freeit; } if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(n, ifp, SND_IN, ip6len); if (error == 0) goto freeit; nd6_na_input(n, off, icmp6len); } else nd6_na_input(n, off, icmp6len); /* m stays. */ break; case ND_REDIRECT: icmp6_ifstat_inc(ifp, ifs6_in_redirect); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_redirect)) goto badlen; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(m, ifp, SND_IN, ip6len); if (error == 0) return (IPPROTO_DONE); icmp6_redirect_input(m, off); } else icmp6_redirect_input(m, off); m = NULL; goto freeit; } if (send_sendso_input_hook != NULL) { error = send_sendso_input_hook(n, ifp, SND_IN, ip6len); if (error == 0) goto freeit; icmp6_redirect_input(n, off); } else icmp6_redirect_input(n, off); /* m stays. */ break; case ICMP6_ROUTER_RENUMBERING: if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && code != ICMP6_ROUTER_RENUMBERING_RESULT) goto badcode; if (icmp6len < sizeof(struct icmp6_router_renum)) goto badlen; break; default: nd6log((LOG_DEBUG, "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", icmp6->icmp6_type, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), ifp ? ifp->if_index : 0)); if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { /* ICMPv6 error: MUST deliver it by spec... */ code = PRC_NCMDS; /* deliver */ } else { /* ICMPv6 informational: MUST not deliver */ break; } deliver: if (icmp6_notify_error(&m, off, icmp6len, code) != 0) { /* In this case, m should've been freed. */ return (IPPROTO_DONE); } break; badcode: ICMP6STAT_INC(icp6s_badcode); break; badlen: ICMP6STAT_INC(icp6s_badlen); break; } /* deliver the packet to appropriate sockets */ icmp6_rip6_input(&m, *offp); return IPPROTO_DONE; freeit: m_freem(m); return IPPROTO_DONE; } static int icmp6_notify_error(struct mbuf **mp, int off, int icmp6len, int code) { struct mbuf *m = *mp; struct icmp6_hdr *icmp6; struct ip6_hdr *eip6; u_int32_t notifymtu; struct sockaddr_in6 icmp6src, icmp6dst; if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { ICMP6STAT_INC(icp6s_tooshort); goto freeit; } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), -1); icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6) + sizeof(struct ip6_hdr)); if (icmp6 == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif eip6 = (struct ip6_hdr *)(icmp6 + 1); /* Detect the upper level protocol */ { void (*ctlfunc)(int, struct sockaddr *, void *); u_int8_t nxt = eip6->ip6_nxt; int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); struct ip6ctlparam ip6cp; struct in6_addr *finaldst = NULL; int icmp6type = icmp6->icmp6_type; struct ip6_frag *fh; struct ip6_rthdr *rth; struct ip6_rthdr0 *rth0; int rthlen; while (1) { /* XXX: should avoid infinite loop explicitly? */ struct ip6_ext *eh; switch (nxt) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_AH: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), -1); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(eh, struct ip6_ext *, m, eoff, sizeof(*eh)); if (eh == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif if (nxt == IPPROTO_AH) eoff += (eh->ip6e_len + 2) << 2; else eoff += (eh->ip6e_len + 1) << 3; nxt = eh->ip6e_nxt; break; case IPPROTO_ROUTING: /* * When the erroneous packet contains a * routing header, we should examine the * header to determine the final destination. * Otherwise, we can't properly update * information that depends on the final * destination (e.g. path MTU). */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), -1); rth = (struct ip6_rthdr *) (mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, eoff, sizeof(*rth)); if (rth == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif rthlen = (rth->ip6r_len + 1) << 3; /* * XXX: currently there is no * officially defined type other * than type-0. * Note that if the segment left field * is 0, all intermediate hops must * have been passed. */ if (rth->ip6r_segleft && rth->ip6r_type == IPV6_RTHDR_TYPE_0) { int hops; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, -1); rth0 = (struct ip6_rthdr0 *) (mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(rth0, struct ip6_rthdr0 *, m, eoff, rthlen); if (rth0 == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif /* just ignore a bogus header */ if ((rth0->ip6r0_len % 2) == 0 && (hops = rth0->ip6r0_len/2)) finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); } eoff += rthlen; nxt = rth->ip6r_nxt; break; case IPPROTO_FRAGMENT: #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_frag), -1); fh = (struct ip6_frag *)(mtod(m, caddr_t) + eoff); #else IP6_EXTHDR_GET(fh, struct ip6_frag *, m, eoff, sizeof(*fh)); if (fh == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif /* * Data after a fragment header is meaningless * unless it is the first fragment, but * we'll go to the notify label for path MTU * discovery. */ if (fh->ip6f_offlg & IP6F_OFF_MASK) goto notify; eoff += sizeof(struct ip6_frag); nxt = fh->ip6f_nxt; break; default: /* * This case includes ESP and the No Next * Header. In such cases going to the notify * label does not have any meaning * (i.e. ctlfunc will be NULL), but we go * anyway since we might have to update * path MTU information. */ goto notify; } } notify: #ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6) + sizeof(struct ip6_hdr)); if (icmp6 == NULL) { ICMP6STAT_INC(icp6s_tooshort); return (-1); } #endif /* * retrieve parameters from the inner IPv6 header, and convert * them into sockaddr structures. * XXX: there is no guarantee that the source or destination * addresses of the inner packet are in the same scope as * the addresses of the icmp packet. But there is no other * way to determine the zone. */ eip6 = (struct ip6_hdr *)(icmp6 + 1); bzero(&icmp6dst, sizeof(icmp6dst)); icmp6dst.sin6_len = sizeof(struct sockaddr_in6); icmp6dst.sin6_family = AF_INET6; if (finaldst == NULL) icmp6dst.sin6_addr = eip6->ip6_dst; else icmp6dst.sin6_addr = *finaldst; if (in6_setscope(&icmp6dst.sin6_addr, m->m_pkthdr.rcvif, NULL)) goto freeit; bzero(&icmp6src, sizeof(icmp6src)); icmp6src.sin6_len = sizeof(struct sockaddr_in6); icmp6src.sin6_family = AF_INET6; icmp6src.sin6_addr = eip6->ip6_src; if (in6_setscope(&icmp6src.sin6_addr, m->m_pkthdr.rcvif, NULL)) goto freeit; icmp6src.sin6_flowinfo = (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); if (finaldst == NULL) finaldst = &eip6->ip6_dst; ip6cp.ip6c_m = m; ip6cp.ip6c_icmp6 = icmp6; ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); ip6cp.ip6c_off = eoff; ip6cp.ip6c_finaldst = finaldst; ip6cp.ip6c_src = &icmp6src; ip6cp.ip6c_nxt = nxt; if (icmp6type == ICMP6_PACKET_TOO_BIG) { notifymtu = ntohl(icmp6->icmp6_mtu); ip6cp.ip6c_cmdarg = (void *)¬ifymtu; icmp6_mtudisc_update(&ip6cp, 1); /*XXX*/ } ctlfunc = (void (*)(int, struct sockaddr *, void *)) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst, &ip6cp); } } *mp = m; return (0); freeit: m_freem(m); return (-1); } void icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated) { struct in6_addr *dst = ip6cp->ip6c_finaldst; struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ u_int mtu = ntohl(icmp6->icmp6_mtu); struct in_conninfo inc; #if 0 /* * RFC2460 section 5, last paragraph. * even though minimum link MTU for IPv6 is IPV6_MMTU, * we may see ICMPv6 too big with mtu < IPV6_MMTU * due to packet translator in the middle. * see ip6_output() and ip6_getpmtu() "alwaysfrag" case for * special handling. */ if (mtu < IPV6_MMTU) return; #endif /* * we reject ICMPv6 too big with abnormally small value. * XXX what is the good definition of "abnormally small"? */ if (mtu < sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + 8) return; if (!validated) return; /* * In case the suggested mtu is less than IPV6_MMTU, we * only need to remember that it was for above mentioned * "alwaysfrag" case. * Try to be as close to the spec as possible. */ if (mtu < IPV6_MMTU) mtu = IPV6_MMTU - 8; bzero(&inc, sizeof(inc)); inc.inc_fibnum = M_GETFIB(m); inc.inc_flags |= INC_ISIPV6; inc.inc6_faddr = *dst; if (in6_setscope(&inc.inc6_faddr, m->m_pkthdr.rcvif, NULL)) return; if (mtu < tcp_maxmtu6(&inc, NULL)) { tcp_hc_updatemtu(&inc, mtu); ICMP6STAT_INC(icp6s_pmtuchg); } } /* * Process a Node Information Query packet, based on * draft-ietf-ipngwg-icmp-name-lookups-07. * * Spec incompatibilities: * - IPv6 Subject address handling * - IPv4 Subject address handling support missing * - Proxy reply (answer even if it's not for me) * - joins NI group address at in6_ifattach() time only, does not cope * with hostname changes by sethostname(3) */ static struct mbuf * ni6_input(struct mbuf *m, int off) { struct icmp6_nodeinfo *ni6, *nni6; struct mbuf *n = NULL; struct prison *pr; u_int16_t qtype; int subjlen; int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); struct ni_reply_fqdn *fqdn; int addrs; /* for NI_QTYPE_NODEADDR */ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ struct in6_addr in6_subj; /* subject address */ struct ip6_hdr *ip6; int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ char *subj = NULL; struct in6_ifaddr *ia6 = NULL; ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); if (ni6 == NULL) { /* m is already reclaimed */ return (NULL); } #endif /* * Validate IPv6 source address. * The default configuration MUST be to refuse answering queries from * global-scope addresses according to RFC4602. * Notes: * - it's not very clear what "refuse" means; this implementation * simply drops it. * - it's not very easy to identify global-scope (unicast) addresses * since there are many prefixes for them. It should be safer * and in practice sufficient to check "all" but loopback and * link-local (note that site-local unicast was deprecated and * ULA is defined as global scope-wise) */ if ((V_icmp6_nodeinfo & ICMP6_NODEINFO_GLOBALOK) == 0 && !IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) && !IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) goto bad; /* * Validate IPv6 destination address. * * The Responder must discard the Query without further processing * unless it is one of the Responder's unicast or anycast addresses, or * a link-local scope multicast address which the Responder has joined. * [RFC4602, Section 5.] */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (!IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) goto bad; /* else it's a link-local multicast, fine */ } else { /* unicast or anycast */ ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); if (ia6 == NULL) goto bad; /* XXX impossible */ if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) && !(V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) { ifa_free(&ia6->ia_ifa); nd6log((LOG_DEBUG, "ni6_input: ignore node info to " "a temporary address in %s:%d", __FILE__, __LINE__)); goto bad; } ifa_free(&ia6->ia_ifa); } /* validate query Subject field. */ qtype = ntohs(ni6->ni_qtype); subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); switch (qtype) { case NI_QTYPE_NOOP: case NI_QTYPE_SUPTYPES: /* 07 draft */ if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0) break; /* FALLTHROUGH */ case NI_QTYPE_FQDN: case NI_QTYPE_NODEADDR: case NI_QTYPE_IPV4ADDR: switch (ni6->ni_code) { case ICMP6_NI_SUBJ_IPV6: #if ICMP6_NI_SUBJ_IPV6 != 0 case 0: #endif /* * backward compatibility - try to accept 03 draft * format, where no Subject is present. */ if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 && subjlen == 0) { oldfqdn++; break; } #if ICMP6_NI_SUBJ_IPV6 != 0 if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6) goto bad; #endif if (subjlen != sizeof(struct in6_addr)) goto bad; /* * Validate Subject address. * * Not sure what exactly "address belongs to the node" * means in the spec, is it just unicast, or what? * * At this moment we consider Subject address as * "belong to the node" if the Subject address equals * to the IPv6 destination address; validation for * IPv6 destination address should have done enough * check for us. * * We do not do proxy at this moment. */ /* m_pulldown instead of copy? */ m_copydata(m, off + sizeof(struct icmp6_nodeinfo), subjlen, (caddr_t)&in6_subj); if (in6_setscope(&in6_subj, m->m_pkthdr.rcvif, NULL)) goto bad; subj = (char *)&in6_subj; if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &in6_subj)) break; /* * XXX if we are to allow other cases, we should really * be careful about scope here. * basically, we should disallow queries toward IPv6 * destination X with subject Y, * if scope(X) > scope(Y). * if we allow scope(X) > scope(Y), it will result in * information leakage across scope boundary. */ goto bad; case ICMP6_NI_SUBJ_FQDN: /* * Validate Subject name with gethostname(3). * * The behavior may need some debate, since: * - we are not sure if the node has FQDN as * hostname (returned by gethostname(3)). * - the code does wildcard match for truncated names. * however, we are not sure if we want to perform * wildcard match, if gethostname(3) side has * truncated hostname. */ pr = curthread->td_ucred->cr_prison; mtx_lock(&pr->pr_mtx); n = ni6_nametodns(pr->pr_hostname, strlen(pr->pr_hostname), 0); mtx_unlock(&pr->pr_mtx); if (!n || n->m_next || n->m_len == 0) goto bad; IP6_EXTHDR_GET(subj, char *, m, off + sizeof(struct icmp6_nodeinfo), subjlen); if (subj == NULL) goto bad; if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *), n->m_len)) { goto bad; } m_freem(n); n = NULL; break; case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */ default: goto bad; } break; } /* refuse based on configuration. XXX ICMP6_NI_REFUSED? */ switch (qtype) { case NI_QTYPE_FQDN: if ((V_icmp6_nodeinfo & ICMP6_NODEINFO_FQDNOK) == 0) goto bad; break; case NI_QTYPE_NODEADDR: case NI_QTYPE_IPV4ADDR: if ((V_icmp6_nodeinfo & ICMP6_NODEINFO_NODEADDROK) == 0) goto bad; break; } /* guess reply length */ switch (qtype) { case NI_QTYPE_NOOP: break; /* no reply data */ case NI_QTYPE_SUPTYPES: replylen += sizeof(u_int32_t); break; case NI_QTYPE_FQDN: /* XXX will append an mbuf */ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); break; case NI_QTYPE_NODEADDR: addrs = ni6_addrs(ni6, m, &ifp, (struct in6_addr *)subj); if ((replylen += addrs * (sizeof(struct in6_addr) + sizeof(u_int32_t))) > MCLBYTES) replylen = MCLBYTES; /* XXX: will truncate pkt later */ break; case NI_QTYPE_IPV4ADDR: /* unsupported - should respond with unknown Qtype? */ break; default: /* * XXX: We must return a reply with the ICMP6 code * `unknown Qtype' in this case. However we regard the case * as an FQDN query for backward compatibility. * Older versions set a random value to this field, * so it rarely varies in the defined qtypes. * But the mechanism is not reliable... * maybe we should obsolete older versions. */ qtype = NI_QTYPE_FQDN; /* XXX will append an mbuf */ replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); oldfqdn++; break; } /* Allocate an mbuf to reply. */ if (replylen > MCLBYTES) { /* * XXX: should we try to allocate more? But MCLBYTES * is probably much larger than IPV6_MMTU... */ goto bad; } if (replylen > MHLEN) n = m_getcl(M_NOWAIT, m->m_type, M_PKTHDR); else n = m_gethdr(M_NOWAIT, m->m_type); if (n == NULL) { m_freem(m); return (NULL); } m_move_pkthdr(n, m); /* just for recvif and FIB */ n->m_pkthdr.len = n->m_len = replylen; /* copy mbuf header and IPv6 + Node Information base headers */ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); /* qtype dependent procedure */ switch (qtype) { case NI_QTYPE_NOOP: nni6->ni_code = ICMP6_NI_SUCCESS; nni6->ni_flags = 0; break; case NI_QTYPE_SUPTYPES: { u_int32_t v; nni6->ni_code = ICMP6_NI_SUCCESS; nni6->ni_flags = htons(0x0000); /* raw bitmap */ /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */ v = (u_int32_t)htonl(0x0000000f); bcopy(&v, nni6 + 1, sizeof(u_int32_t)); break; } case NI_QTYPE_FQDN: nni6->ni_code = ICMP6_NI_SUCCESS; fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo)); nni6->ni_flags = 0; /* XXX: meaningless TTL */ fqdn->ni_fqdn_ttl = 0; /* ditto. */ /* * XXX do we really have FQDN in hostname? */ pr = curthread->td_ucred->cr_prison; mtx_lock(&pr->pr_mtx); n->m_next = ni6_nametodns(pr->pr_hostname, strlen(pr->pr_hostname), oldfqdn); mtx_unlock(&pr->pr_mtx); if (n->m_next == NULL) goto bad; /* XXX we assume that n->m_next is not a chain */ if (n->m_next->m_next != NULL) goto bad; n->m_pkthdr.len += n->m_next->m_len; break; case NI_QTYPE_NODEADDR: { int lenlim, copied; nni6->ni_code = ICMP6_NI_SUCCESS; n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); lenlim = M_TRAILINGSPACE(n); copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); /* XXX: reset mbuf length */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo) + copied; break; } default: break; /* XXX impossible! */ } nni6->ni_type = ICMP6_NI_REPLY; m_freem(m); return (n); bad: m_freem(m); if (n) m_freem(n); return (NULL); } /* * make a mbuf with DNS-encoded string. no compression support. * * XXX names with less than 2 dots (like "foo" or "foo.section") will be * treated as truncated name (two \0 at the end). this is a wild guess. * * old - return pascal string if non-zero */ static struct mbuf * ni6_nametodns(const char *name, int namelen, int old) { struct mbuf *m; char *cp, *ep; const char *p, *q; int i, len, nterm; if (old) len = namelen + 1; else len = MCLBYTES; /* Because MAXHOSTNAMELEN is usually 256, we use cluster mbuf. */ if (len > MLEN) m = m_getcl(M_NOWAIT, MT_DATA, 0); else m = m_get(M_NOWAIT, MT_DATA); if (m == NULL) goto fail; if (old) { m->m_len = len; *mtod(m, char *) = namelen; bcopy(name, mtod(m, char *) + 1, namelen); return m; } else { m->m_len = 0; cp = mtod(m, char *); ep = mtod(m, char *) + M_TRAILINGSPACE(m); /* if not certain about my name, return empty buffer */ if (namelen == 0) return m; /* * guess if it looks like shortened hostname, or FQDN. * shortened hostname needs two trailing "\0". */ i = 0; for (p = name; p < name + namelen; p++) { if (*p && *p == '.') i++; } if (i < 2) nterm = 2; else nterm = 1; p = name; while (cp < ep && p < name + namelen) { i = 0; for (q = p; q < name + namelen && *q && *q != '.'; q++) i++; /* result does not fit into mbuf */ if (cp + i + 1 >= ep) goto fail; /* * DNS label length restriction, RFC1035 page 8. * "i == 0" case is included here to avoid returning * 0-length label on "foo..bar". */ if (i <= 0 || i >= 64) goto fail; *cp++ = i; bcopy(p, cp, i); cp += i; p = q; if (p < name + namelen && *p == '.') p++; } /* termination */ if (cp + nterm >= ep) goto fail; while (nterm-- > 0) *cp++ = '\0'; m->m_len = cp - mtod(m, char *); return m; } panic("should not reach here"); /* NOTREACHED */ fail: if (m) m_freem(m); return NULL; } /* * check if two DNS-encoded string matches. takes care of truncated * form (with \0\0 at the end). no compression support. * XXX upper/lowercase match (see RFC2065) */ static int ni6_dnsmatch(const char *a, int alen, const char *b, int blen) { const char *a0, *b0; int l; /* simplest case - need validation? */ if (alen == blen && bcmp(a, b, alen) == 0) return 1; a0 = a; b0 = b; /* termination is mandatory */ if (alen < 2 || blen < 2) return 0; if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0') return 0; alen--; blen--; while (a - a0 < alen && b - b0 < blen) { if (a - a0 + 1 > alen || b - b0 + 1 > blen) return 0; if ((signed char)a[0] < 0 || (signed char)b[0] < 0) return 0; /* we don't support compression yet */ if (a[0] >= 64 || b[0] >= 64) return 0; /* truncated case */ if (a[0] == 0 && a - a0 == alen - 1) return 1; if (b[0] == 0 && b - b0 == blen - 1) return 1; if (a[0] == 0 || b[0] == 0) return 0; if (a[0] != b[0]) return 0; l = a[0]; if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen) return 0; if (bcmp(a + 1, b + 1, l) != 0) return 0; a += 1 + l; b += 1 + l; } if (a - a0 == alen && b - b0 == blen) return 1; else return 0; } /* * calculate the number of addresses to be returned in the node info reply. */ static int ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, struct ifnet **ifpp, struct in6_addr *subj) { struct ifnet *ifp; struct in6_ifaddr *ifa6; struct ifaddr *ifa; int addrs = 0, addrsofif, iffound = 0; int niflags = ni6->ni_flags; if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { switch (ni6->ni_code) { case ICMP6_NI_SUBJ_IPV6: if (subj == NULL) /* must be impossible... */ return (0); break; default: /* * XXX: we only support IPv6 subject address for * this Qtype. */ return (0); } } IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { addrsofif = 0; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 && IN6_ARE_ADDR_EQUAL(subj, &ifa6->ia_addr.sin6_addr)) iffound = 1; /* * IPv4-mapped addresses can only be returned by a * Node Information proxy, since they represent * addresses of IPv4-only nodes, which perforce do * not implement this protocol. * [icmp-name-lookups-07, Section 5.4] * So we don't support NI_NODEADDR_FLAG_COMPAT in * this function at this moment. */ /* What do we have to do about ::1? */ switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) continue; break; case IPV6_ADDR_SCOPE_SITELOCAL: if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) continue; break; case IPV6_ADDR_SCOPE_GLOBAL: if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) continue; break; default: continue; } /* * check if anycast is okay. * XXX: just experimental. not in the spec. */ if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) continue; /* we need only unicast addresses */ if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && (V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK) == 0) { continue; } addrsofif++; /* count the address */ } IF_ADDR_RUNLOCK(ifp); if (iffound) { *ifpp = ifp; IFNET_RUNLOCK_NOSLEEP(); return (addrsofif); } addrs += addrsofif; } IFNET_RUNLOCK_NOSLEEP(); return (addrs); } static int ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, struct ifnet *ifp0, int resid) { struct ifnet *ifp; struct in6_ifaddr *ifa6; struct ifaddr *ifa; struct ifnet *ifp_dep = NULL; int copied = 0, allow_deprecated = 0; u_char *cp = (u_char *)(nni6 + 1); int niflags = ni6->ni_flags; u_int32_t ltime; if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) return (0); /* needless to copy */ IFNET_RLOCK_NOSLEEP(); ifp = ifp0 ? ifp0 : TAILQ_FIRST(&V_ifnet); again: for (; ifp; ifp = TAILQ_NEXT(ifp, if_link)) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 && allow_deprecated == 0) { /* * prefererred address should be put before * deprecated addresses. */ /* record the interface for later search */ if (ifp_dep == NULL) ifp_dep = ifp; continue; } else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 && allow_deprecated != 0) continue; /* we now collect deprecated addrs */ /* What do we have to do about ::1? */ switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) continue; break; case IPV6_ADDR_SCOPE_SITELOCAL: if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) continue; break; case IPV6_ADDR_SCOPE_GLOBAL: if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) continue; break; default: continue; } /* * check if anycast is okay. * XXX: just experimental. not in the spec. */ if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 && (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) continue; if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && (V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK) == 0) { continue; } /* now we can copy the address */ if (resid < sizeof(struct in6_addr) + sizeof(u_int32_t)) { IF_ADDR_RUNLOCK(ifp); /* * We give up much more copy. * Set the truncate flag and return. */ nni6->ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; IFNET_RUNLOCK_NOSLEEP(); return (copied); } /* * Set the TTL of the address. * The TTL value should be one of the following * according to the specification: * * 1. The remaining lifetime of a DHCP lease on the * address, or * 2. The remaining Valid Lifetime of a prefix from * which the address was derived through Stateless * Autoconfiguration. * * Note that we currently do not support stateful * address configuration by DHCPv6, so the former * case can't happen. */ if (ifa6->ia6_lifetime.ia6t_expire == 0) ltime = ND6_INFINITE_LIFETIME; else { if (ifa6->ia6_lifetime.ia6t_expire > time_uptime) ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - time_uptime); else ltime = 0; } bcopy(<ime, cp, sizeof(u_int32_t)); cp += sizeof(u_int32_t); /* copy the address itself */ bcopy(&ifa6->ia_addr.sin6_addr, cp, sizeof(struct in6_addr)); in6_clearscope((struct in6_addr *)cp); /* XXX */ cp += sizeof(struct in6_addr); resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); copied += (sizeof(struct in6_addr) + sizeof(u_int32_t)); } IF_ADDR_RUNLOCK(ifp); if (ifp0) /* we need search only on the specified IF */ break; } if (allow_deprecated == 0 && ifp_dep != NULL) { ifp = ifp_dep; allow_deprecated = 1; goto again; } IFNET_RUNLOCK_NOSLEEP(); return (copied); } /* * XXX almost dup'ed code with rip6_input. */ static int icmp6_rip6_input(struct mbuf **mp, int off) { struct mbuf *m = *mp; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct inpcb *in6p; struct inpcb *last = NULL; struct sockaddr_in6 fromsa; struct icmp6_hdr *icmp6; struct mbuf *opts = NULL; #ifndef PULLDOWN_TEST /* this is assumed to be safe. */ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); if (icmp6 == NULL) { /* m is already reclaimed */ return (IPPROTO_DONE); } #endif /* * XXX: the address may have embedded scope zone ID, which should be * hidden from applications. */ bzero(&fromsa, sizeof(fromsa)); fromsa.sin6_family = AF_INET6; fromsa.sin6_len = sizeof(struct sockaddr_in6); fromsa.sin6_addr = ip6->ip6_src; if (sa6_recoverscope(&fromsa)) { m_freem(m); return (IPPROTO_DONE); } INP_INFO_RLOCK(&V_ripcbinfo); LIST_FOREACH(in6p, &V_ripcb, inp_list) { if ((in6p->inp_vflag & INP_IPV6) == 0) continue; if (in6p->inp_ip_p != IPPROTO_ICMPV6) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; INP_RLOCK(in6p); if (ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, in6p->in6p_icmp6filt)) { INP_RUNLOCK(in6p); continue; } if (last != NULL) { struct mbuf *n = NULL; /* * Recent network drivers tend to allocate a single * mbuf cluster, rather than to make a couple of * mbufs without clusters. Also, since the IPv6 code * path tries to avoid m_pullup(), it is highly * probable that we still have an mbuf cluster here * even though the necessary length can be stored in an * mbuf's internal buffer. * Meanwhile, the default size of the receive socket * buffer for raw sockets is not so large. This means * the possibility of packet loss is relatively higher * than before. To avoid this scenario, we copy the * received data to a separate mbuf that does not use * a cluster, if possible. * XXX: it is better to copy the data after stripping * intermediate headers. */ if ((m->m_flags & M_EXT) && m->m_next == NULL && m->m_len <= MHLEN) { n = m_get(M_NOWAIT, m->m_type); if (n != NULL) { if (m_dup_pkthdr(n, m, M_NOWAIT)) { bcopy(m->m_data, n->m_data, m->m_len); n->m_len = m->m_len; } else { m_free(n); n = NULL; } } } if (n != NULL || (n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { if (last->inp_flags & INP_CONTROLOPTS) ip6_savecontrol(last, n, &opts); /* strip intermediate headers */ m_adj(n, off); SOCKBUF_LOCK(&last->inp_socket->so_rcv); if (sbappendaddr_locked( &last->inp_socket->so_rcv, (struct sockaddr *)&fromsa, n, opts) == 0) { /* should notify about lost packet */ m_freem(n); if (opts) { m_freem(opts); } SOCKBUF_UNLOCK( &last->inp_socket->so_rcv); } else sorwakeup_locked(last->inp_socket); opts = NULL; } INP_RUNLOCK(last); } last = in6p; } INP_INFO_RUNLOCK(&V_ripcbinfo); if (last != NULL) { if (last->inp_flags & INP_CONTROLOPTS) ip6_savecontrol(last, m, &opts); /* strip intermediate headers */ m_adj(m, off); /* avoid using mbuf clusters if possible (see above) */ if ((m->m_flags & M_EXT) && m->m_next == NULL && m->m_len <= MHLEN) { struct mbuf *n; n = m_get(M_NOWAIT, m->m_type); if (n != NULL) { if (m_dup_pkthdr(n, m, M_NOWAIT)) { bcopy(m->m_data, n->m_data, m->m_len); n->m_len = m->m_len; m_freem(m); m = n; } else { m_freem(n); n = NULL; } } } SOCKBUF_LOCK(&last->inp_socket->so_rcv); if (sbappendaddr_locked(&last->inp_socket->so_rcv, (struct sockaddr *)&fromsa, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); SOCKBUF_UNLOCK(&last->inp_socket->so_rcv); } else sorwakeup_locked(last->inp_socket); INP_RUNLOCK(last); } else { m_freem(m); IP6STAT_DEC(ip6s_delivered); } return IPPROTO_DONE; } /* * Reflect the ip6 packet back to the source. * OFF points to the icmp6 header, counted from the top of the mbuf. */ void icmp6_reflect(struct mbuf *m, size_t off) { struct in6_addr src, *srcp = NULL; struct ip6_hdr *ip6; struct icmp6_hdr *icmp6; struct in6_ifaddr *ia = NULL; struct ifnet *outif = NULL; int plen; int type, code; /* too short to reflect */ if (off < sizeof(struct ip6_hdr)) { nd6log((LOG_DEBUG, "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", (u_long)off, (u_long)sizeof(struct ip6_hdr), __FILE__, __LINE__)); goto bad; } /* * If there are extra headers between IPv6 and ICMPv6, strip * off that header first. */ #ifdef DIAGNOSTIC if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) panic("assumption failed in icmp6_reflect"); #endif if (off > sizeof(struct ip6_hdr)) { size_t l; struct ip6_hdr nip6; l = off - sizeof(struct ip6_hdr); m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6); m_adj(m, l); l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); if (m->m_len < l) { if ((m = m_pullup(m, l)) == NULL) return; } bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6)); } else /* off == sizeof(struct ip6_hdr) */ { size_t l; l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); if (m->m_len < l) { if ((m = m_pullup(m, l)) == NULL) return; } } plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_nxt = IPPROTO_ICMPV6; icmp6 = (struct icmp6_hdr *)(ip6 + 1); type = icmp6->icmp6_type; /* keep type for statistics */ code = icmp6->icmp6_code; /* ditto. */ /* * If the incoming packet was addressed directly to us (i.e. unicast), * use dst as the src for the reply. * The IN6_IFF_NOTREADY case should be VERY rare, but is possible * (for example) when we encounter an error while forwarding procedure * destined to a duplicated address of ours. */ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); if (ia != NULL && !(ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) srcp = &ia->ia_addr.sin6_addr; } if (srcp == NULL) { int e; struct sockaddr_in6 sin6; struct route_in6 ro; /* * This case matches to multicasts, our anycast, or unicasts * that we do not own. Select a source address based on the * source address of the erroneous packet. */ bzero(&sin6, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(sin6); sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */ bzero(&ro, sizeof(ro)); e = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, &outif, &src); if (ro.ro_rt) RTFREE(ro.ro_rt); /* XXX: we could use this */ if (e) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "icmp6_reflect: source can't be determined: " "dst=%s, error=%d\n", ip6_sprintf(ip6buf, &sin6.sin6_addr), e)); goto bad; } srcp = &src; } /* * ip6_input() drops a packet if its src is multicast. * So, the src is never multicast. */ ip6->ip6_dst = ip6->ip6_src; ip6->ip6_src = *srcp; ip6->ip6_flow = 0; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; if (outif) ip6->ip6_hlim = ND_IFINFO(outif)->chlim; else if (m->m_pkthdr.rcvif) { /* XXX: This may not be the outgoing interface */ ip6->ip6_hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim; } else ip6->ip6_hlim = V_ip6_defhlim; icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), plen); /* * XXX option handling */ m->m_flags &= ~(M_BCAST|M_MCAST); ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); if (outif) icmp6_ifoutstat_inc(outif, type, code); if (ia != NULL) ifa_free(&ia->ia_ifa); return; bad: if (ia != NULL) ifa_free(&ia->ia_ifa); m_freem(m); return; } void icmp6_fasttimo(void) { mld_fasttimo(); } void icmp6_slowtimo(void) { mld_slowtimo(); } static const char * icmp6_redirect_diag(struct in6_addr *src6, struct in6_addr *dst6, struct in6_addr *tgt6) { static char buf[1024]; char ip6bufs[INET6_ADDRSTRLEN]; char ip6bufd[INET6_ADDRSTRLEN]; char ip6buft[INET6_ADDRSTRLEN]; snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", ip6_sprintf(ip6bufs, src6), ip6_sprintf(ip6bufd, dst6), ip6_sprintf(ip6buft, tgt6)); return buf; } void icmp6_redirect_input(struct mbuf *m, int off) { struct ifnet *ifp; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_redirect *nd_rd; int icmp6len = ntohs(ip6->ip6_plen); char *lladdr = NULL; int lladdrlen = 0; int is_router; int is_onlink; struct in6_addr src6 = ip6->ip6_src; struct in6_addr redtgt6; struct in6_addr reddst6; union nd_opts ndopts; char ip6buf[INET6_ADDRSTRLEN]; M_ASSERTPKTHDR(m); KASSERT(m->m_pkthdr.rcvif != NULL, ("%s: no rcvif", __func__)); ifp = m->m_pkthdr.rcvif; /* XXX if we are router, we don't update route by icmp6 redirect */ if (V_ip6_forwarding) goto freeit; if (!V_icmp6_rediraccept) goto freeit; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); if (nd_rd == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif redtgt6 = nd_rd->nd_rd_target; reddst6 = nd_rd->nd_rd_dst; if (in6_setscope(&redtgt6, m->m_pkthdr.rcvif, NULL) || in6_setscope(&reddst6, m->m_pkthdr.rcvif, NULL)) { goto freeit; } /* validation */ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " "must be from linklocal\n", ip6_sprintf(ip6buf, &src6))); goto bad; } if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", ip6_sprintf(ip6buf, &src6), ip6->ip6_hlim)); goto bad; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ struct nhop6_basic nh6; struct in6_addr kdst; uint32_t scopeid; in6_splitscope(&reddst6, &kdst, &scopeid); if (fib6_lookup_nh_basic(RT_DEFAULT_FIB, &kdst, scopeid, 0, 0,&nh6)==0){ if ((nh6.nh_flags & NHF_GATEWAY) == 0) { nd6log((LOG_ERR, "ICMP6 redirect rejected; no route " "with inet6 gateway found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } if (IN6_ARE_ADDR_EQUAL(&src6, &nh6.nh_addr) == 0) { nd6log((LOG_ERR, "ICMP6 redirect rejected; " "not equal to gw-for-src=%s (must be same): " "%s\n", ip6_sprintf(ip6buf, &nh6.nh_addr), icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } } else { nd6log((LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&reddst6)) { nd6log((LOG_ERR, "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } is_router = is_onlink = 0; if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) is_router = 1; /* router case */ if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) is_onlink = 1; /* on-link destination case */ if (!is_router && !is_onlink) { nd6log((LOG_ERR, "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } icmp6len -= sizeof(*nd_rd); nd6_option_init(nd_rd + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "%s: invalid ND option, rejected: %s\n", __func__, icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s " "(if %d, icmp6 packet %d): %s\n", __func__, ip6_sprintf(ip6buf, &redtgt6), ifp->if_addrlen, lladdrlen - 2, icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } /* Validation passed. */ /* RFC 2461 8.3 */ nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); /* * Install a gateway route in the better-router case or an interface * route in the on-link-destination case. */ { struct sockaddr_in6 sdst; struct sockaddr_in6 sgw; struct sockaddr_in6 ssrc; struct sockaddr *gw; int rt_flags; u_int fibnum; bzero(&sdst, sizeof(sdst)); bzero(&ssrc, sizeof(ssrc)); sdst.sin6_family = ssrc.sin6_family = AF_INET6; sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); rt_flags = RTF_HOST; if (is_router) { bzero(&sgw, sizeof(sgw)); sgw.sin6_family = AF_INET6; sgw.sin6_len = sizeof(struct sockaddr_in6); bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); gw = (struct sockaddr *)&sgw; rt_flags |= RTF_GATEWAY; } else gw = ifp->if_addr->ifa_addr; for (fibnum = 0; fibnum < rt_numfibs; fibnum++) in6_rtredirect((struct sockaddr *)&sdst, gw, (struct sockaddr *)NULL, rt_flags, (struct sockaddr *)&ssrc, fibnum); } /* finally update cached route in each socket via pfctlinput */ { struct sockaddr_in6 sdst; bzero(&sdst, sizeof(sdst)); sdst.sin6_family = AF_INET6; sdst.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); } freeit: m_freem(m); return; bad: ICMP6STAT_INC(icp6s_badredirect); m_freem(m); } void icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) { struct ifnet *ifp; /* my outgoing interface */ struct in6_addr *ifp_ll6; struct in6_addr *router_ll6; struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ struct mbuf *m = NULL; /* newly allocated one */ struct m_tag *mtag; struct ip6_hdr *ip6; /* m as struct ip6_hdr */ struct nd_redirect *nd_rd; struct llentry *ln = NULL; size_t maxlen; u_char *p; struct ifnet *outif = NULL; struct sockaddr_in6 src_sa; icmp6_errcount(ND_REDIRECT, 0); /* if we are not router, we don't send icmp6 redirect */ if (!V_ip6_forwarding) goto fail; /* sanity check */ if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) goto fail; /* * Address check: * the source address must identify a neighbor, and * the destination address must not be a multicast address * [RFC 2461, sec 8.2] */ sip6 = mtod(m0, struct ip6_hdr *); bzero(&src_sa, sizeof(src_sa)); src_sa.sin6_family = AF_INET6; src_sa.sin6_len = sizeof(src_sa); src_sa.sin6_addr = sip6->ip6_src; if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) goto fail; if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) goto fail; /* what should we do here? */ /* rate limit */ if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) goto fail; /* * Since we are going to append up to 1280 bytes (= IPV6_MMTU), * we almost always ask for an mbuf cluster for simplicity. * (MHLEN < IPV6_MMTU is almost always true) */ #if IPV6_MMTU >= MCLBYTES # error assumption failed about IPV6_MMTU and MCLBYTES #endif m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) goto fail; M_SETFIB(m, rt->rt_fibnum); maxlen = M_TRAILINGSPACE(m); maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { goto fail; } { /* get ip6 linklocal address for ifp(my outgoing interface). */ struct in6_ifaddr *ia; if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY| IN6_IFF_ANYCAST)) == NULL) goto fail; ifp_ll6 = &ia->ia_addr.sin6_addr; /* XXXRW: reference released prematurely. */ ifa_free(&ia->ia_ifa); } /* get ip6 linklocal address for the router. */ if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)rt->rt_gateway; router_ll6 = &sin6->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) router_ll6 = (struct in6_addr *)NULL; } else router_ll6 = (struct in6_addr *)NULL; /* ip6 */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; /* ip6->ip6_src must be linklocal addr for my outgoing if. */ bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); /* ND Redirect */ nd_rd = (struct nd_redirect *)(ip6 + 1); nd_rd->nd_rd_type = ND_REDIRECT; nd_rd->nd_rd_code = 0; nd_rd->nd_rd_reserved = 0; if (rt->rt_flags & RTF_GATEWAY) { /* * nd_rd->nd_rd_target must be a link-local address in * better router cases. */ if (!router_ll6) goto fail; bcopy(router_ll6, &nd_rd->nd_rd_target, sizeof(nd_rd->nd_rd_target)); bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, sizeof(nd_rd->nd_rd_dst)); } else { /* make sure redtgt == reddst */ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, sizeof(nd_rd->nd_rd_target)); bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, sizeof(nd_rd->nd_rd_dst)); } p = (u_char *)(nd_rd + 1); if (!router_ll6) goto nolladdropt; { /* target lladdr option */ int len; struct nd_opt_hdr *nd_opt; char *lladdr; IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(router_ll6, 0, ifp); IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) goto nolladdropt; len = sizeof(*nd_opt) + ifp->if_addrlen; len = (len + 7) & ~7; /* round by 8 */ /* safety check */ if (len + (p - (u_char *)ip6) > maxlen) goto nolladdropt; if (ln->la_flags & LLE_VALID) { nd_opt = (struct nd_opt_hdr *)p; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = len >> 3; lladdr = (char *)(nd_opt + 1); - bcopy(&ln->ll_addr, lladdr, ifp->if_addrlen); + bcopy(ln->ll_addr, lladdr, ifp->if_addrlen); p += len; } } nolladdropt: if (ln != NULL) LLE_RUNLOCK(ln); m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* just to be safe */ #ifdef M_DECRYPTED /*not openbsd*/ if (m0->m_flags & M_DECRYPTED) goto noredhdropt; #endif if (p - (u_char *)ip6 > maxlen) goto noredhdropt; { /* redirected header option */ int len; struct nd_opt_rd_hdr *nd_opt_rh; /* * compute the maximum size for icmp6 redirect header option. * XXX room for auth header? */ len = maxlen - (p - (u_char *)ip6); len &= ~7; /* This is just for simplicity. */ if (m0->m_pkthdr.len != m0->m_len) { if (m0->m_next) { m_freem(m0->m_next); m0->m_next = NULL; } m0->m_pkthdr.len = m0->m_len; } /* * Redirected header option spec (RFC2461 4.6.3) talks nothing * about padding/truncate rule for the original IP packet. * From the discussion on IPv6imp in Feb 1999, * the consensus was: * - "attach as much as possible" is the goal * - pad if not aligned (original size can be guessed by * original ip6 header) * Following code adds the padding if it is simple enough, * and truncates if not. */ if (m0->m_next || m0->m_pkthdr.len != m0->m_len) panic("assumption failed in %s:%d", __FILE__, __LINE__); if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { /* not enough room, truncate */ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); } else { /* enough room, pad or truncate */ size_t extra; extra = m0->m_pkthdr.len % 8; if (extra) { /* pad if easy enough, truncate if not */ if (8 - extra <= M_TRAILINGSPACE(m0)) { /* pad */ m0->m_len += (8 - extra); m0->m_pkthdr.len += (8 - extra); } else { /* truncate */ m0->m_pkthdr.len -= extra; m0->m_len -= extra; } } len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); } nd_opt_rh = (struct nd_opt_rd_hdr *)p; bzero(nd_opt_rh, sizeof(*nd_opt_rh)); nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; nd_opt_rh->nd_opt_rh_len = len >> 3; p += sizeof(*nd_opt_rh); m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* connect m0 to m */ m_tag_delete_chain(m0, NULL); m0->m_flags &= ~M_PKTHDR; m->m_next = m0; m->m_pkthdr.len = m->m_len + m0->m_len; m0 = NULL; } noredhdropt:; if (m0) { m_freem(m0); m0 = NULL; } /* XXX: clear embedded link IDs in the inner header */ in6_clearscope(&sip6->ip6_src); in6_clearscope(&sip6->ip6_dst); in6_clearscope(&nd_rd->nd_rd_target); in6_clearscope(&nd_rd->nd_rd_dst); ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); nd_rd->nd_rd_cksum = 0; nd_rd->nd_rd_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); if (send_sendso_input_hook != NULL) { mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, sizeof(unsigned short), M_NOWAIT); if (mtag == NULL) goto fail; *(unsigned short *)(mtag + 1) = nd_rd->nd_rd_type; m_tag_prepend(m, mtag); } /* send the packet to outside... */ ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_redirect); } ICMP6STAT_INC(icp6s_outhist[ND_REDIRECT]); return; fail: if (m) m_freem(m); if (m0) m_freem(m0); } /* * ICMPv6 socket option processing. */ int icmp6_ctloutput(struct socket *so, struct sockopt *sopt) { int error = 0; int optlen; struct inpcb *inp = sotoinpcb(so); int level, op, optname; if (sopt) { level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; } else level = op = optname = optlen = 0; if (level != IPPROTO_ICMPV6) { return EINVAL; } switch (op) { case PRCO_SETOPT: switch (optname) { case ICMP6_FILTER: { struct icmp6_filter ic6f; if (optlen != sizeof(ic6f)) { error = EMSGSIZE; break; } error = sooptcopyin(sopt, &ic6f, optlen, optlen); if (error == 0) { INP_WLOCK(inp); *inp->in6p_icmp6filt = ic6f; INP_WUNLOCK(inp); } break; } default: error = ENOPROTOOPT; break; } break; case PRCO_GETOPT: switch (optname) { case ICMP6_FILTER: { struct icmp6_filter ic6f; INP_RLOCK(inp); ic6f = *inp->in6p_icmp6filt; INP_RUNLOCK(inp); error = sooptcopyout(sopt, &ic6f, sizeof(ic6f)); break; } default: error = ENOPROTOOPT; break; } break; } return (error); } /* * Perform rate limit check. * Returns 0 if it is okay to send the icmp6 packet. * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate * limitation. * * XXX per-destination/type check necessary? * * dst - not used at this moment * type - not used at this moment * code - not used at this moment */ static int icmp6_ratelimit(const struct in6_addr *dst, const int type, const int code) { int ret; ret = 0; /* okay to send */ /* PPS limit */ if (!ppsratecheck(&V_icmp6errppslim_last, &V_icmp6errpps_count, V_icmp6errppslim)) { /* The packet is subject to rate limit */ ret++; } return ret; } Index: projects/clang380-import/sys/netinet6/in6.c =================================================================== --- projects/clang380-import/sys/netinet6/in6.c (revision 293015) +++ projects/clang380-import/sys/netinet6/in6.c (revision 293016) @@ -1,2524 +1,2532 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi Exp $ */ /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.c 8.2 (Berkeley) 11/15/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_inet.h" #include "opt_inet6.h" #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 #include #include #include #include #include #include #include #include #include #include #include #include VNET_DECLARE(int, icmp6_nodeinfo_oldmcprefix); #define V_icmp6_nodeinfo_oldmcprefix VNET(icmp6_nodeinfo_oldmcprefix) /* * Definitions of some costant IP6 addresses. */ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; const struct in6_addr in6addr_linklocal_allv2routers = IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT; const struct in6_addr in6mask0 = IN6MASK0; const struct in6_addr in6mask32 = IN6MASK32; const struct in6_addr in6mask64 = IN6MASK64; const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; const struct sockaddr_in6 sa6_any = { sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; static int in6_notify_ifa(struct ifnet *, struct in6_ifaddr *, struct in6_aliasreq *, int); static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int); static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *, struct in6_aliasreq *, int flags); static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int, int); static int in6_broadcast_ifa(struct ifnet *, struct in6_aliasreq *, struct in6_ifaddr *, int); #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) #define ia62ifa(ia6) (&((ia6)->ia_ifa)) void in6_newaddrmsg(struct in6_ifaddr *ia, int cmd) { struct sockaddr_dl gateway; struct sockaddr_in6 mask, addr; struct rtentry rt; /* * initialize for rtmsg generation */ bzero(&gateway, sizeof(gateway)); gateway.sdl_len = sizeof(gateway); gateway.sdl_family = AF_LINK; bzero(&rt, sizeof(rt)); rt.rt_gateway = (struct sockaddr *)&gateway; memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); rt_mask(&rt) = (struct sockaddr *)&mask; rt_key(&rt) = (struct sockaddr *)&addr; rt.rt_flags = RTF_HOST | RTF_STATIC; if (cmd == RTM_ADD) rt.rt_flags |= RTF_UP; /* Announce arrival of local address to all FIBs. */ rt_newaddrmsg(cmd, &ia->ia_ifa, 0, &rt); } int in6_mask2len(struct in6_addr *mask, u_char *lim0) { int x = 0, y; u_char *lim = lim0, *p; /* ignore the scope_id part */ if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) lim = (u_char *)mask + sizeof(*mask); for (p = (u_char *)mask; p < lim; x++, p++) { if (*p != 0xff) break; } y = 0; if (p < lim) { for (y = 0; y < 8; y++) { if ((*p & (0x80 >> y)) == 0) break; } } /* * when the limit pointer is given, do a stricter check on the * remaining bits. */ if (p < lim) { if (y != 0 && (*p & (0x00ff >> y)) != 0) return (-1); for (p = p + 1; p < lim; p++) if (*p != 0) return (-1); } return x * 8 + y; } #ifdef COMPAT_FREEBSD32 struct in6_ndifreq32 { char ifname[IFNAMSIZ]; uint32_t ifindex; }; #define SIOCGDEFIFACE32_IN6 _IOWR('i', 86, struct in6_ndifreq32) #endif int in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { struct in6_ifreq *ifr = (struct in6_ifreq *)data; struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct sockaddr_in6 *sa6; int carp_attached = 0; int error; u_long ocmd = cmd; /* * Compat to make pre-10.x ifconfig(8) operable. */ if (cmd == OSIOCAIFADDR_IN6) cmd = SIOCAIFADDR_IN6; switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: /* * XXX mrt_ioctl has a 3rd, unused, FIB argument in route.c. * We cannot see how that would be needed, so do not adjust the * KPI blindly; more likely should clean up the IPv4 variant. */ return (mrt6_ioctl ? mrt6_ioctl(cmd, data) : EOPNOTSUPP); } switch (cmd) { case SIOCAADDRCTL_POLICY: case SIOCDADDRCTL_POLICY: if (td != NULL) { error = priv_check(td, PRIV_NETINET_ADDRCTRL6); if (error) return (error); } return (in6_src_ioctl(cmd, data)); } if (ifp == NULL) return (EOPNOTSUPP); switch (cmd) { case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: case SIOCSDEFIFACE_IN6: case SIOCSIFINFO_FLAGS: case SIOCSIFINFO_IN6: if (td != NULL) { error = priv_check(td, PRIV_NETINET_ND6); if (error) return (error); } /* FALLTHROUGH */ case OSIOCGIFINFO_IN6: case SIOCGIFINFO_IN6: case SIOCGNBRINFO_IN6: case SIOCGDEFIFACE_IN6: return (nd6_ioctl(cmd, data, ifp)); #ifdef COMPAT_FREEBSD32 case SIOCGDEFIFACE32_IN6: { struct in6_ndifreq ndif; struct in6_ndifreq32 *ndif32; error = nd6_ioctl(SIOCGDEFIFACE_IN6, (caddr_t)&ndif, ifp); if (error) return (error); ndif32 = (struct in6_ndifreq32 *)data; ndif32->ifindex = ndif.ifindex; return (0); } #endif } switch (cmd) { case SIOCSIFPREFIX_IN6: case SIOCDIFPREFIX_IN6: case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: case SIOCGIFPREFIX_IN6: log(LOG_NOTICE, "prefix ioctls are now invalidated. " "please use ifconfig.\n"); return (EOPNOTSUPP); } switch (cmd) { case SIOCSSCOPE6: if (td != NULL) { error = priv_check(td, PRIV_NETINET_SCOPE6); if (error) return (error); } /* FALLTHROUGH */ case SIOCGSCOPE6: case SIOCGSCOPE6DEF: return (scope6_ioctl(cmd, data, ifp)); } /* * Find address for this interface, if it exists. * * In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation * only, and used the first interface address as the target of other * operations (without checking ifra_addr). This was because netinet * code/API assumed at most 1 interface address per interface. * Since IPv6 allows a node to assign multiple addresses * on a single interface, we almost always look and check the * presence of ifra_addr, and reject invalid ones here. * It also decreases duplicated code among SIOC*_IN6 operations. */ switch (cmd) { case SIOCAIFADDR_IN6: case SIOCSIFPHYADDR_IN6: sa6 = &ifra->ifra_addr; break; case SIOCSIFADDR_IN6: case SIOCGIFADDR_IN6: case SIOCSIFDSTADDR_IN6: case SIOCSIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFNETMASK_IN6: case SIOCDIFADDR_IN6: case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: case SIOCGIFAFLAG_IN6: case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: case SIOCGIFALIFETIME_IN6: case SIOCSIFALIFETIME_IN6: case SIOCGIFSTAT_IN6: case SIOCGIFSTAT_ICMP6: sa6 = &ifr->ifr_addr; break; case SIOCSIFADDR: case SIOCSIFBRDADDR: case SIOCSIFDSTADDR: case SIOCSIFNETMASK: /* * Although we should pass any non-INET6 ioctl requests * down to driver, we filter some legacy INET requests. * Drivers trust SIOCSIFADDR et al to come from an already * privileged layer, and do not perform any credentials * checks or input validation. */ return (EINVAL); default: sa6 = NULL; break; } if (sa6 && sa6->sin6_family == AF_INET6) { if (sa6->sin6_scope_id != 0) error = sa6_embedscope(sa6, 0); else error = in6_setscope(&sa6->sin6_addr, ifp, NULL); if (error != 0) return (error); if (td != NULL && (error = prison_check_ip6(td->td_ucred, &sa6->sin6_addr)) != 0) return (error); ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); } else ia = NULL; switch (cmd) { case SIOCSIFADDR_IN6: case SIOCSIFDSTADDR_IN6: case SIOCSIFNETMASK_IN6: /* * Since IPv6 allows a node to assign multiple addresses * on a single interface, SIOCSIFxxx ioctls are deprecated. */ /* we decided to obsolete this command (20000704) */ error = EINVAL; goto out; case SIOCDIFADDR_IN6: /* * for IPv4, we look for existing in_ifaddr here to allow * "ifconfig if0 delete" to remove the first IPv4 address on * the interface. For IPv6, as the spec allows multiple * interface address from the day one, we consider "remove the * first one" semantics to be not preferable. */ if (ia == NULL) { error = EADDRNOTAVAIL; goto out; } /* FALLTHROUGH */ case SIOCAIFADDR_IN6: /* * We always require users to specify a valid IPv6 address for * the corresponding operation. */ if (ifra->ifra_addr.sin6_family != AF_INET6 || ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) { error = EAFNOSUPPORT; goto out; } if (td != NULL) { error = priv_check(td, (cmd == SIOCDIFADDR_IN6) ? PRIV_NET_DELIFADDR : PRIV_NET_ADDIFADDR); if (error) goto out; } /* FALLTHROUGH */ case SIOCGIFSTAT_IN6: case SIOCGIFSTAT_ICMP6: if (ifp->if_afdata[AF_INET6] == NULL) { error = EPFNOSUPPORT; goto out; } break; case SIOCGIFADDR_IN6: /* This interface is basically deprecated. use SIOCGIFCONF. */ /* FALLTHROUGH */ case SIOCGIFAFLAG_IN6: case SIOCGIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFALIFETIME_IN6: /* must think again about its semantics */ if (ia == NULL) { error = EADDRNOTAVAIL; goto out; } break; case SIOCSIFALIFETIME_IN6: { struct in6_addrlifetime *lt; if (td != NULL) { error = priv_check(td, PRIV_NETINET_ALIFETIME6); if (error) goto out; } if (ia == NULL) { error = EADDRNOTAVAIL; goto out; } /* sanity for overflow - beware unsigned */ lt = &ifr->ifr_ifru.ifru_lifetime; if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME && lt->ia6t_vltime + time_uptime < time_uptime) { error = EINVAL; goto out; } if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME && lt->ia6t_pltime + time_uptime < time_uptime) { error = EINVAL; goto out; } break; } } switch (cmd) { case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0) goto out; break; case SIOCGIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { error = EINVAL; goto out; } /* * XXX: should we check if ifa_dstaddr is NULL and return * an error? */ ifr->ifr_dstaddr = ia->ia_dstaddr; if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0) goto out; break; case SIOCGIFNETMASK_IN6: ifr->ifr_addr = ia->ia_prefixmask; break; case SIOCGIFAFLAG_IN6: ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; break; case SIOCGIFSTAT_IN6: COUNTER_ARRAY_COPY(((struct in6_ifextra *) ifp->if_afdata[AF_INET6])->in6_ifstat, &ifr->ifr_ifru.ifru_stat, sizeof(struct in6_ifstat) / sizeof(uint64_t)); break; case SIOCGIFSTAT_ICMP6: COUNTER_ARRAY_COPY(((struct in6_ifextra *) ifp->if_afdata[AF_INET6])->icmp6_ifstat, &ifr->ifr_ifru.ifru_icmp6stat, sizeof(struct icmp6_ifstat) / sizeof(uint64_t)); break; case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { time_t maxexpire; struct in6_addrlifetime *retlt = &ifr->ifr_ifru.ifru_lifetime; /* * XXX: adjust expiration time assuming time_t is * signed. */ maxexpire = (-1) & ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); if (ia->ia6_lifetime.ia6t_vltime < maxexpire - ia->ia6_updatetime) { retlt->ia6t_expire = ia->ia6_updatetime + ia->ia6_lifetime.ia6t_vltime; } else retlt->ia6t_expire = maxexpire; } if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { time_t maxexpire; struct in6_addrlifetime *retlt = &ifr->ifr_ifru.ifru_lifetime; /* * XXX: adjust expiration time assuming time_t is * signed. */ maxexpire = (-1) & ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); if (ia->ia6_lifetime.ia6t_pltime < maxexpire - ia->ia6_updatetime) { retlt->ia6t_preferred = ia->ia6_updatetime + ia->ia6_lifetime.ia6t_pltime; } else retlt->ia6t_preferred = maxexpire; } break; case SIOCSIFALIFETIME_IN6: ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime; /* for sanity */ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_uptime + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_uptime + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; break; case SIOCAIFADDR_IN6: { struct nd_prefixctl pr0; struct nd_prefix *pr; /* * first, make or update the interface address structure, * and link it to the list. */ if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0) goto out; if (ia != NULL) ifa_free(&ia->ia_ifa); if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) == NULL) { /* * this can happen when the user specify the 0 valid * lifetime. */ break; } if (cmd == ocmd && ifra->ifra_vhid > 0) { if (carp_attach_p != NULL) error = (*carp_attach_p)(&ia->ia_ifa, ifra->ifra_vhid); else error = EPROTONOSUPPORT; if (error) goto out; else carp_attached = 1; } /* * then, make the prefix on-link on the interface. * XXX: we'd rather create the prefix before the address, but * we need at least one address to install the corresponding * interface route, so we configure the address first. */ /* * convert mask to prefix length (prefixmask has already * been validated in in6_update_ifa(). */ bzero(&pr0, sizeof(pr0)); pr0.ndpr_ifp = ifp; pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL); if (pr0.ndpr_plen == 128) { /* we don't need to install a host route. */ goto aifaddr_out; } pr0.ndpr_prefix = ifra->ifra_addr; /* apply the mask for safety. */ IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr, &ifra->ifra_prefixmask.sin6_addr); /* * XXX: since we don't have an API to set prefix (not address) * lifetimes, we just use the same lifetimes as addresses. * The (temporarily) installed lifetimes can be overridden by * later advertised RAs (when accept_rtadv is non 0), which is * an intended behavior. */ pr0.ndpr_raf_onlink = 1; /* should be configurable? */ pr0.ndpr_raf_auto = ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; /* add the prefix if not yet. */ if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { /* * nd6_prelist_add will install the corresponding * interface route. */ if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) { if (carp_attached) (*carp_detach_p)(&ia->ia_ifa); goto out; } if (pr == NULL) { if (carp_attached) (*carp_detach_p)(&ia->ia_ifa); log(LOG_ERR, "nd6_prelist_add succeeded but " "no prefix\n"); error = EINVAL; goto out; } } /* relate the address to the prefix */ if (ia->ia6_ndpr == NULL) { ia->ia6_ndpr = pr; pr->ndpr_refcnt++; /* * If this is the first autoconf address from the * prefix, create a temporary address as well * (when required). */ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && V_ip6_use_tempaddr && pr->ndpr_refcnt == 1) { int e; if ((e = in6_tmpifadd(ia, 1, 0)) != 0) { log(LOG_NOTICE, "in6_control: failed " "to create a temporary address, " "errno=%d\n", e); } } } /* * this might affect the status of autoconfigured addresses, * that is, this address might make other addresses detached. */ pfxlist_onlink_check(); aifaddr_out: if (error != 0 || ia == NULL) break; /* * Try to clear the flag when a new IPv6 address is added * onto an IFDISABLED interface and it succeeds. */ if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { struct in6_ndireq nd; memset(&nd, 0, sizeof(nd)); nd.ndi.flags = ND_IFINFO(ifp)->flags; nd.ndi.flags &= ~ND6_IFF_IFDISABLED; if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0) log(LOG_NOTICE, "SIOCAIFADDR_IN6: " "SIOCSIFINFO_FLAGS for -ifdisabled " "failed."); /* * Ignore failure of clearing the flag intentionally. * The failure means address duplication was detected. */ } EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } case SIOCDIFADDR_IN6: { struct nd_prefix *pr; /* * If the address being deleted is the only one that owns * the corresponding prefix, expire the prefix as well. * XXX: theoretically, we don't have to worry about such * relationship, since we separate the address management * and the prefix management. We do this, however, to provide * as much backward compatibility as possible in terms of * the ioctl operation. * Note that in6_purgeaddr() will decrement ndpr_refcnt. */ pr = ia->ia6_ndpr; in6_purgeaddr(&ia->ia_ifa); if (pr && pr->ndpr_refcnt == 0) prelist_remove(pr); EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } default: if (ifp->if_ioctl == NULL) { error = EOPNOTSUPP; goto out; } error = (*ifp->if_ioctl)(ifp, cmd, data); goto out; } error = 0; out: if (ia != NULL) ifa_free(&ia->ia_ifa); return (error); } /* * Join necessary multicast groups. Factored out from in6_update_ifa(). * This entire work should only be done once, for the default FIB. */ static int in6_update_ifa_join_mc(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags, struct in6_multi **in6m_sol) { char ip6buf[INET6_ADDRSTRLEN]; struct in6_addr mltaddr; struct in6_multi_mship *imm; int delay, error; KASSERT(in6m_sol != NULL, ("%s: in6m_sol is NULL", __func__)); /* Join solicited multicast addr for new host id. */ bzero(&mltaddr, sizeof(struct in6_addr)); mltaddr.s6_addr32[0] = IPV6_ADDR_INT32_MLL; mltaddr.s6_addr32[2] = htonl(1); mltaddr.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; mltaddr.s6_addr8[12] = 0xff; if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0) { /* XXX: should not happen */ log(LOG_ERR, "%s: in6_setscope failed\n", __func__); goto cleanup; } delay = error = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * We need a random delay for DAD on the address being * configured. It also means delaying transmission of the * corresponding MLD report to avoid report collision. * [RFC 4861, Section 6.3.7] */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } imm = in6_joingroup(ifp, &mltaddr, &error, delay); if (imm == NULL) { nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr), if_name(ifp), error)); goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); *in6m_sol = imm->i6mm_maddr; /* * Join link-local all-nodes address. */ mltaddr = in6addr_linklocal_allnodes; if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0) goto cleanup; /* XXX: should not fail */ imm = in6_joingroup(ifp, &mltaddr, &error, 0); if (imm == NULL) { nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr), if_name(ifp), error)); goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); /* * Join node information group address. */ delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * The spec does not say anything about delay for this group, * but the same logic should apply. */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } if (in6_nigroup(ifp, NULL, -1, &mltaddr) == 0) { /* XXX jinmei */ imm = in6_joingroup(ifp, &mltaddr, &error, delay); if (imm == NULL) nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr), if_name(ifp), error)); /* XXX not very fatal, go on... */ else LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } if (V_icmp6_nodeinfo_oldmcprefix && in6_nigroup_oldmcprefix(ifp, NULL, -1, &mltaddr) == 0) { imm = in6_joingroup(ifp, &mltaddr, &error, delay); if (imm == NULL) nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr), if_name(ifp), error)); /* XXX not very fatal, go on... */ else LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } /* * Join interface-local all-nodes address. * (ff01::1%ifN, and ff01::%ifN/32) */ mltaddr = in6addr_nodelocal_allnodes; if ((error = in6_setscope(&mltaddr, ifp, NULL)) != 0) goto cleanup; /* XXX: should not fail */ imm = in6_joingroup(ifp, &mltaddr, &error, 0); if (imm == NULL) { nd6log((LOG_WARNING, "%s: in6_joingroup failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mltaddr), if_name(ifp), error)); goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); cleanup: return (error); } /* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. * This function is separated from in6_control(). */ int in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { int error, hostIsNew = 0; if ((error = in6_validate_ifra(ifp, ifra, ia, flags)) != 0) return (error); if (ia == NULL) { hostIsNew = 1; if ((ia = in6_alloc_ifa(ifp, ifra, flags)) == NULL) return (ENOBUFS); } error = in6_update_ifa_internal(ifp, ifra, ia, hostIsNew, flags); if (error != 0) { if (hostIsNew != 0) { in6_unlink_ifa(ia, ifp); ifa_free(&ia->ia_ifa); } return (error); } if (hostIsNew) error = in6_broadcast_ifa(ifp, ifra, ia, flags); return (error); } /* * Fill in basic IPv6 address request info. */ void in6_prepare_ifra(struct in6_aliasreq *ifra, const struct in6_addr *addr, const struct in6_addr *mask) { memset(ifra, 0, sizeof(struct in6_aliasreq)); ifra->ifra_addr.sin6_family = AF_INET6; ifra->ifra_addr.sin6_len = sizeof(struct sockaddr_in6); if (addr != NULL) ifra->ifra_addr.sin6_addr = *addr; ifra->ifra_prefixmask.sin6_family = AF_INET6; ifra->ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); if (mask != NULL) ifra->ifra_prefixmask.sin6_addr = *mask; } static int in6_validate_ifra(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { int plen = -1; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ if (ifp == NULL || ifra == NULL) /* this maybe redundant */ return (EINVAL); /* * The destination address for a p2p link must have a family * of AF_UNSPEC or AF_INET6. */ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && ifra->ifra_dstaddr.sin6_family != AF_INET6 && ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) return (EAFNOSUPPORT); /* * Validate address */ if (ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6) || ifra->ifra_addr.sin6_family != AF_INET6) return (EINVAL); /* * validate ifra_prefixmask. don't check sin6_family, netmask * does not carry fields other than sin6_len. */ if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) return (EINVAL); /* * Because the IPv6 address architecture is classless, we require * users to specify a (non 0) prefix length (mask) for a new address. * We also require the prefix (when specified) mask is valid, and thus * reject a non-consecutive mask. */ if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0) return (EINVAL); if (ifra->ifra_prefixmask.sin6_len != 0) { plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, (u_char *)&ifra->ifra_prefixmask + ifra->ifra_prefixmask.sin6_len); if (plen <= 0) return (EINVAL); } else { /* * In this case, ia must not be NULL. We just use its prefix * length. */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); } /* * If the destination address on a p2p interface is specified, * and the address is a scoped one, validate/set the scope * zone identifier. */ dst6 = ifra->ifra_dstaddr; if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 && (dst6.sin6_family == AF_INET6)) { struct in6_addr in6_tmp; u_int32_t zoneid; in6_tmp = dst6.sin6_addr; if (in6_setscope(&in6_tmp, ifp, &zoneid)) return (EINVAL); /* XXX: should be impossible */ if (dst6.sin6_scope_id != 0) { if (dst6.sin6_scope_id != zoneid) return (EINVAL); } else /* user omit to specify the ID. */ dst6.sin6_scope_id = zoneid; /* convert into the internal form */ if (sa6_embedscope(&dst6, 0)) return (EINVAL); /* XXX: should be impossible */ } /* Modify original ifra_dstaddr to reflect changes */ ifra->ifra_dstaddr = dst6; /* * The destination address can be specified only for a p2p or a * loopback interface. If specified, the corresponding prefix length * must be 128. */ if (ifra->ifra_dstaddr.sin6_family == AF_INET6) { if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { /* XXX: noisy message */ nd6log((LOG_INFO, "in6_update_ifa: a destination can " "be specified for a p2p or a loopback IF only\n")); return (EINVAL); } if (plen != 128) { nd6log((LOG_INFO, "in6_update_ifa: prefixlen should " "be 128 when dstaddr is specified\n")); return (EINVAL); } } /* lifetime consistency check */ lt = &ifra->ifra_lifetime; if (lt->ia6t_pltime > lt->ia6t_vltime) return (EINVAL); if (lt->ia6t_vltime == 0) { /* * the following log might be noisy, but this is a typical * configuration mistake or a tool's bug. */ nd6log((LOG_INFO, "in6_update_ifa: valid lifetime is 0 for %s\n", ip6_sprintf(ip6buf, &ifra->ifra_addr.sin6_addr))); if (ia == NULL) return (0); /* there's nothing to do */ } /* Check prefix mask */ if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) { /* * We prohibit changing the prefix length of an existing * address, because * + such an operation should be rare in IPv6, and * + the operation would confuse prefix management. */ if (ia->ia_prefixmask.sin6_len != 0 && in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { nd6log((LOG_INFO, "in6_validate_ifa: the prefix length " "of an existing %s address should not be changed\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); return (EINVAL); } } return (0); } /* * Allocate a new ifaddr and link it into chains. */ static struct in6_ifaddr * in6_alloc_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int flags) { struct in6_ifaddr *ia; /* * When in6_alloc_ifa() is called in a process of a received * RA, it is called under an interrupt context. So, we should * call malloc with M_NOWAIT. */ ia = (struct in6_ifaddr *)ifa_alloc(sizeof(*ia), M_NOWAIT); if (ia == NULL) return (NULL); LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; ia->ia_addr.sin6_family = AF_INET6; ia->ia_addr.sin6_len = sizeof(ia->ia_addr); /* XXX: Can we assign ,sin6_addr and skip the rest? */ ia->ia_addr = ifra->ifra_addr; ia->ia6_createtime = time_uptime; if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { /* * Some functions expect that ifa_dstaddr is not * NULL for p2p interfaces. */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; } else { ia->ia_ifa.ifa_dstaddr = NULL; } /* set prefix mask if any */ ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; if (ifra->ifra_prefixmask.sin6_len != 0) { ia->ia_prefixmask.sin6_family = AF_INET6; ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len; ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; } ia->ia_ifp = ifp; ifa_ref(&ia->ia_ifa); /* if_addrhead */ IF_ADDR_WLOCK(ifp); TAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_ref(&ia->ia_ifa); /* in6_ifaddrhead */ IN6_IFADDR_WLOCK(); TAILQ_INSERT_TAIL(&V_in6_ifaddrhead, ia, ia_link); LIST_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia, ia6_hash); IN6_IFADDR_WUNLOCK(); return (ia); } /* * Update/configure interface address parameters: * * 1) Update lifetime * 2) Update interface metric ad flags * 3) Notify other subsystems */ static int in6_update_ifa_internal(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int hostIsNew, int flags) { int error; /* update timestamp */ ia->ia6_updatetime = time_uptime; /* * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred * to see if the address is deprecated or invalidated, but initialize * these members for applications. */ ia->ia6_lifetime = ifra->ifra_lifetime; if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_uptime + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_uptime + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; /* * backward compatibility - if IN6_IFF_DEPRECATED is set from the * userland, make it deprecated. */ if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) { ia->ia6_lifetime.ia6t_pltime = 0; ia->ia6_lifetime.ia6t_preferred = time_uptime; } /* * configure address flags. */ ia->ia6_flags = ifra->ifra_flags; /* * Make the address tentative before joining multicast addresses, * so that corresponding MLD responses would not have a tentative * source address. */ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */ /* * DAD should be performed for an new address or addresses on * an interface with ND6_IFF_IFDISABLED. */ if (in6if_do_dad(ifp) && (hostIsNew || (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED))) ia->ia6_flags |= IN6_IFF_TENTATIVE; /* notify other subsystems */ error = in6_notify_ifa(ifp, ia, ifra, hostIsNew); return (error); } /* * Do link-level ifa job: * 1) Add lle entry for added address * 2) Notifies routing socket users about new address * 3) join appropriate multicast group * 4) start DAD if enabled */ static int in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { struct in6_multi *in6m_sol; int error = 0; /* Add local address to lltable, if necessary (ex. on p2p link). */ if ((error = nd6_add_ifa_lle(ia)) != 0) { in6_purgeaddr(&ia->ia_ifa); ifa_free(&ia->ia_ifa); return (error); } /* Join necessary multicast groups. */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol); if (error != 0) { in6_purgeaddr(&ia->ia_ifa); ifa_free(&ia->ia_ifa); return (error); } } /* Perform DAD, if the address is TENTATIVE. */ if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) { int delay, mindelay, maxdelay; delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * We need to impose a delay before sending an NS * for DAD. Check if we also needed a delay for the * corresponding MLD message. If we did, the delay * should be larger than the MLD delay (this could be * relaxed a bit, but this simple logic is at least * safe). * XXX: Break data hiding guidelines and look at * state for the solicited multicast group. */ mindelay = 0; if (in6m_sol != NULL && in6m_sol->in6m_state == MLD_REPORTING_MEMBER) { mindelay = in6m_sol->in6m_timer; } maxdelay = MAX_RTR_SOLICITATION_DELAY * hz; if (maxdelay - mindelay == 0) delay = 0; else { delay = (arc4random() % (maxdelay - mindelay)) + mindelay; } } nd6_dad_start((struct ifaddr *)ia, delay); } in6_newaddrmsg(ia, RTM_ADD); ifa_free(&ia->ia_ifa); return (error); } void in6_purgeaddr(struct ifaddr *ifa) { struct ifnet *ifp = ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; struct in6_multi_mship *imm; int plen, error; if (ifa->ifa_carp) (*carp_detach_p)(ifa); /* * Remove the loopback route to the interface address. * The check for the current setting of "nd6_useloopback" * is not needed. */ if (ia->ia_flags & IFA_RTSELF) { error = ifa_del_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); if (error == 0) ia->ia_flags &= ~IFA_RTSELF; } /* stop DAD processing */ nd6_dad_stop(ifa); /* Leave multicast groups. */ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); in6_leavegroup(imm); } plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if ((ia->ia_flags & IFA_ROUTE) && plen == 128) { error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags | (ia->ia_dstaddr.sin6_family == AF_INET6) ? RTF_HOST : 0); if (error != 0) log(LOG_INFO, "%s: err=%d, destination address delete " "failed\n", __func__, error); ia->ia_flags &= ~IFA_ROUTE; } in6_newaddrmsg(ia, RTM_DELETE); in6_unlink_ifa(ia, ifp); } static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { char ip6buf[INET6_ADDRSTRLEN]; int remove_lle; IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); IF_ADDR_WUNLOCK(ifp); ifa_free(&ia->ia_ifa); /* if_addrhead */ /* * Defer the release of what might be the last reference to the * in6_ifaddr so that it can't be freed before the remainder of the * cleanup. */ IN6_IFADDR_WLOCK(); TAILQ_REMOVE(&V_in6_ifaddrhead, ia, ia_link); LIST_REMOVE(ia, ia6_hash); IN6_IFADDR_WUNLOCK(); /* * Release the reference to the base prefix. There should be a * positive reference. */ remove_lle = 0; if (ia->ia6_ndpr == NULL) { nd6log((LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia)))); } else { ia->ia6_ndpr->ndpr_refcnt--; /* Do not delete lles within prefix if refcont != 0 */ if (ia->ia6_ndpr->ndpr_refcnt == 0) remove_lle = 1; ia->ia6_ndpr = NULL; } nd6_rem_ifa_lle(ia, remove_lle); /* * Also, if the address being removed is autoconf'ed, call * pfxlist_onlink_check() since the release might affect the status of * other (detached) addresses. */ if ((ia->ia6_flags & IN6_IFF_AUTOCONF)) { pfxlist_onlink_check(); } ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ } /* * Notifies other subsystems about address change/arrival: * 1) Notifies device handler on the first IPv6 address assignment * 2) Handle routing table changes for P2P links and route * 3) Handle routing table changes for address host route */ static int in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, struct in6_aliasreq *ifra, int hostIsNew) { int error = 0, plen, ifacount = 0; struct ifaddr *ifa; struct sockaddr_in6 *pdst; char ip6buf[INET6_ADDRSTRLEN]; /* * Give the interface a chance to initialize * if this is its first address, */ if (hostIsNew != 0) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifacount++; } IF_ADDR_RUNLOCK(ifp); } if (ifacount <= 1 && ifp->if_ioctl) { error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); if (error) return (error); } /* * If a new destination address is specified, scrub the old one and * install the new destination. Note that the interface must be * p2p or loopback. */ pdst = &ifra->ifra_dstaddr; if (pdst->sin6_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { if ((ia->ia_flags & IFA_ROUTE) != 0 && (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) { nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " "remove a route to the old destination: %s\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); /* proceed anyway... */ } else ia->ia_flags &= ~IFA_ROUTE; ia->ia_dstaddr = *pdst; } /* * If a new destination address is specified for a point-to-point * interface, install a route to the destination as an interface * direct route. * XXX: the logic below rejects assigning multiple addresses on a p2p * interface that share the same destination. */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { int rtflags = RTF_UP | RTF_HOST; /* * Handle the case for ::1 . */ if (ifp->if_flags & IFF_LOOPBACK) ia->ia_flags |= IFA_RTSELF; error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags); if (error) return (error); ia->ia_flags |= IFA_ROUTE; } /* * add a loopback route to self if not exists */ if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); if (error == 0) ia->ia_flags |= IFA_RTSELF; } return (error); } /* * Find an IPv6 interface link-local address specific to an interface. * ifaddr is returned referenced. */ struct in6_ifaddr * in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) { struct ifaddr *ifa; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) { if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0) continue; ifa_ref(ifa); break; } } IF_ADDR_RUNLOCK(ifp); return ((struct in6_ifaddr *)ifa); } /* * find the internet address corresponding to a given address. * ifaddr is returned referenced. */ struct in6_ifaddr * in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) { struct rm_priotracker in6_ifa_tracker; struct in6_ifaddr *ia; IN6_IFADDR_RLOCK(&in6_ifa_tracker); LIST_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) { if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), addr)) { if (zoneid != 0 && zoneid != ia->ia_addr.sin6_scope_id) continue; ifa_ref(&ia->ia_ifa); break; } } IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (ia); } /* * find the internet address corresponding to a given interface and address. * ifaddr is returned referenced. */ struct in6_ifaddr * in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr) { struct ifaddr *ifa; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) { ifa_ref(ifa); break; } } IF_ADDR_RUNLOCK(ifp); return ((struct in6_ifaddr *)ifa); } /* * Find a link-local scoped address on ifp and return it if any. */ struct in6_ifaddr * in6ifa_llaonifp(struct ifnet *ifp) { struct sockaddr_in6 *sin6; struct ifaddr *ifa; if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) return (NULL); - if_addr_rlock(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&sin6->sin6_addr)) break; } - if_addr_runlock(ifp); + IF_ADDR_RUNLOCK(ifp); return ((struct in6_ifaddr *)ifa); } /* * Convert IP6 address to printable (loggable) representation. Caller * has to make sure that ip6buf is at least INET6_ADDRSTRLEN long. */ static char digits[] = "0123456789abcdef"; char * ip6_sprintf(char *ip6buf, const struct in6_addr *addr) { int i, cnt = 0, maxcnt = 0, idx = 0, index = 0; char *cp; const u_int16_t *a = (const u_int16_t *)addr; const u_int8_t *d; int dcolon = 0, zero = 0; cp = ip6buf; for (i = 0; i < 8; i++) { if (*(a + i) == 0) { cnt++; if (cnt == 1) idx = i; } else if (maxcnt < cnt) { maxcnt = cnt; index = idx; cnt = 0; } } if (maxcnt < cnt) { maxcnt = cnt; index = idx; } for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0 && i == index) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (const u_char *)a; /* Try to eliminate leading zeros in printout like in :0001. */ zero = 1; *cp = digits[*d >> 4]; if (*cp != '0') { zero = 0; cp++; } *cp = digits[*d++ & 0xf]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp = digits[*d >> 4]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp++ = digits[*d & 0xf]; *cp++ = ':'; a++; } *--cp = '\0'; return (ip6buf); } int in6_localaddr(struct in6_addr *in6) { struct rm_priotracker in6_ifa_tracker; struct in6_ifaddr *ia; if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) return 1; IN6_IFADDR_RLOCK(&in6_ifa_tracker); TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, &ia->ia_prefixmask.sin6_addr)) { IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return 1; } } IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (0); } /* * Return 1 if an internet address is for the local host and configured * on one of its interfaces. */ int in6_localip(struct in6_addr *in6) { struct rm_priotracker in6_ifa_tracker; struct in6_ifaddr *ia; IN6_IFADDR_RLOCK(&in6_ifa_tracker); LIST_FOREACH(ia, IN6ADDR_HASH(in6), ia6_hash) { if (IN6_ARE_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr)) { IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (1); } } IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (0); } /* * Return 1 if an internet address is configured on an interface. */ int in6_ifhasaddr(struct ifnet *ifp, struct in6_addr *addr) { struct in6_addr in6; struct ifaddr *ifa; struct in6_ifaddr *ia6; in6 = *addr; if (in6_clearscope(&in6)) return (0); in6_setscope(&in6, ifp, NULL); IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia6 = (struct in6_ifaddr *)ifa; if (IN6_ARE_ADDR_EQUAL(&ia6->ia_addr.sin6_addr, &in6)) { IF_ADDR_RUNLOCK(ifp); return (1); } } IF_ADDR_RUNLOCK(ifp); return (0); } int in6_is_addr_deprecated(struct sockaddr_in6 *sa6) { struct rm_priotracker in6_ifa_tracker; struct in6_ifaddr *ia; IN6_IFADDR_RLOCK(&in6_ifa_tracker); LIST_FOREACH(ia, IN6ADDR_HASH(&sa6->sin6_addr), ia6_hash) { if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), &sa6->sin6_addr)) { if (ia->ia6_flags & IN6_IFF_DEPRECATED) { IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (1); /* true */ } break; } } IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); return (0); /* false */ } /* * return length of part which dst and src are equal * hard coding... */ int in6_matchlen(struct in6_addr *src, struct in6_addr *dst) { int match = 0; u_char *s = (u_char *)src, *d = (u_char *)dst; u_char *lim = s + 16, r; while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < 128) { match++; r <<= 1; } break; } else match += 8; return match; } /* XXX: to be scope conscious */ int in6_are_prefix_equal(struct in6_addr *p1, struct in6_addr *p2, int len) { int bytelen, bitlen; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", len); return (0); } bytelen = len / 8; bitlen = len % 8; if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) return (0); if (bitlen != 0 && p1->s6_addr[bytelen] >> (8 - bitlen) != p2->s6_addr[bytelen] >> (8 - bitlen)) return (0); return (1); } void in6_prefixlen2mask(struct in6_addr *maskp, int len) { u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", len); return; } bzero(maskp, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; for (i = 0; i < bytelen; i++) maskp->s6_addr[i] = 0xff; if (bitlen) maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; } /* * return the best address out of the same scope. if no address was * found, return the first valid address from designated IF. */ struct in6_ifaddr * in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) { int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = 0; struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ dep[0] = dep[1] = NULL; /* * We first look for addresses in the same scope. * If there is one, return it. * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (V_ip6_use_deprecated) dep[0] = (struct in6_ifaddr *)ifa; continue; } if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { /* * call in6_matchlen() as few as possible */ if (besta) { if (blen == -1) blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); tlen = in6_matchlen(IFA_IN6(ifa), dst); if (tlen > blen) { blen = tlen; besta = (struct in6_ifaddr *)ifa; } } else besta = (struct in6_ifaddr *)ifa; } } if (besta) { ifa_ref(&besta->ia_ifa); IF_ADDR_RUNLOCK(ifp); return (besta); } TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (V_ip6_use_deprecated) dep[1] = (struct in6_ifaddr *)ifa; continue; } if (ifa != NULL) ifa_ref(ifa); IF_ADDR_RUNLOCK(ifp); return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) { ifa_ref((struct ifaddr *)dep[0]); IF_ADDR_RUNLOCK(ifp); return dep[0]; } if (dep[1]) { ifa_ref((struct ifaddr *)dep[1]); IF_ADDR_RUNLOCK(ifp); return dep[1]; } IF_ADDR_RUNLOCK(ifp); return NULL; } /* * perform DAD when interface becomes IFF_UP. */ void in6_if_up(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if (ia->ia6_flags & IN6_IFF_TENTATIVE) { /* * The TENTATIVE flag was likely set by hand * beforehand, implicitly indicating the need for DAD. * We may be able to skip the random delay in this * case, but we impose delays just in case. */ nd6_dad_start(ifa, arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); } } IF_ADDR_RUNLOCK(ifp); /* * special cases, like 6to4, are handled in in6_ifattach */ in6_ifattach(ifp, NULL); } int in6if_do_dad(struct ifnet *ifp) { if ((ifp->if_flags & IFF_LOOPBACK) != 0) return (0); if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) || (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD)) return (0); /* * Our DAD routine requires the interface up and running. * However, some interfaces can be up before the RUNNING * status. Additionaly, users may try to assign addresses * before the interface becomes up (or running). * This function returns EAGAIN in that case. * The caller should mark "tentative" on the address instead of * performing DAD immediately. */ if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) return (EAGAIN); return (1); } /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. */ void in6_setmaxmtu(void) { unsigned long maxmtu = 0; struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) continue; if ((ifp->if_flags & IFF_LOOPBACK) == 0 && IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } IFNET_RUNLOCK_NOSLEEP(); if (maxmtu) /* update only when maxmtu is positive */ V_in6_maxmtu = maxmtu; } /* * Provide the length of interface identifiers to be used for the link attached * to the given interface. The length should be defined in "IPv6 over * xxx-link" document. Note that address architecture might also define * the length for a particular set of address prefixes, regardless of the * link type. As clarified in rfc2462bis, those two definitions should be * consistent, and those really are as of August 2004. */ int in6_if2idlen(struct ifnet *ifp) { switch (ifp->if_type) { case IFT_ETHER: /* RFC2464 */ case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */ case IFT_L2VLAN: /* ditto */ case IFT_IEEE80211: /* ditto */ case IFT_BRIDGE: /* bridge(4) only does Ethernet-like links */ case IFT_INFINIBAND: return (64); case IFT_FDDI: /* RFC2467 */ return (64); case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */ return (64); case IFT_PPP: /* RFC2472 */ return (64); case IFT_ARCNET: /* RFC2497 */ return (64); case IFT_FRELAY: /* RFC2590 */ return (64); case IFT_IEEE1394: /* RFC3146 */ return (64); case IFT_GIF: return (64); /* draft-ietf-v6ops-mech-v2-07 */ case IFT_LOOP: return (64); /* XXX: is this really correct? */ default: /* * Unknown link type: * It might be controversial to use the today's common constant * of 64 for these cases unconditionally. For full compliance, * we should return an error in this case. On the other hand, * if we simply miss the standard for the link type or a new * standard is defined for a new link type, the IFID length * is very likely to be the common constant. As a compromise, * we always use the constant, but make an explicit notice * indicating the "unknown" case. */ printf("in6_if2idlen: unknown link type (%d)\n", ifp->if_type); return (64); } } #include struct in6_llentry { struct llentry base; }; #define IN6_LLTBL_DEFAULT_HSIZE 32 #define IN6_LLTBL_HASH(k, h) \ (((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1)) /* * Do actual deallocation of @lle. * Called by LLE_FREE_LOCKED when number of references * drops to zero. */ static void in6_lltable_destroy_lle(struct llentry *lle) { LLE_WUNLOCK(lle); LLE_LOCK_DESTROY(lle); LLE_REQ_DESTROY(lle); free(lle, M_LLTABLE); } static struct llentry * in6_lltable_new(const struct in6_addr *addr6, u_int flags) { struct in6_llentry *lle; lle = malloc(sizeof(struct in6_llentry), M_LLTABLE, M_NOWAIT | M_ZERO); if (lle == NULL) /* NB: caller generates msg */ return NULL; lle->base.r_l3addr.addr6 = *addr6; lle->base.lle_refcnt = 1; lle->base.lle_free = in6_lltable_destroy_lle; LLE_LOCK_INIT(&lle->base); LLE_REQ_INIT(&lle->base); callout_init(&lle->base.lle_timer, 1); return (&lle->base); } static int in6_lltable_match_prefix(const struct sockaddr *saddr, const struct sockaddr *smask, u_int flags, struct llentry *lle) { const struct in6_addr *addr, *mask, *lle_addr; addr = &((const struct sockaddr_in6 *)saddr)->sin6_addr; mask = &((const struct sockaddr_in6 *)smask)->sin6_addr; lle_addr = &lle->r_l3addr.addr6; if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) return (0); if (lle->la_flags & LLE_IFADDR) { /* * Delete LLE_IFADDR records IFF address & flag matches. * Note that addr is the interface address within prefix * being matched. */ if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) && (flags & LLE_STATIC) != 0) return (1); return (0); } /* flags & LLE_STATIC means deleting both dynamic and static entries */ if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) return (1); return (0); } static void in6_lltable_free_entry(struct lltable *llt, struct llentry *lle) { struct ifnet *ifp; LLE_WLOCK_ASSERT(lle); KASSERT(llt != NULL, ("lltable is NULL")); /* Unlink entry from table */ if ((lle->la_flags & LLE_LINKED) != 0) { ifp = llt->llt_ifp; IF_AFDATA_WLOCK_ASSERT(ifp); lltable_unlink_entry(llt, lle); } if (callout_stop(&lle->lle_timer) > 0) LLE_REMREF(lle); llentry_free(lle); } static int in6_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in6 *sin6; struct nhop6_basic nh6; struct in6_addr dst; uint32_t scopeid; int error; char ip6buf[INET6_ADDRSTRLEN]; KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); /* Our local addresses are always only installed on the default FIB. */ sin6 = (const struct sockaddr_in6 *)l3addr; in6_splitscope(&sin6->sin6_addr, &dst, &scopeid); error = fib6_lookup_nh_basic(RT_DEFAULT_FIB, &dst, scopeid, 0, 0, &nh6); if (error != 0 || (nh6.nh_flags & NHF_GATEWAY) || nh6.nh_ifp != ifp) { struct ifaddr *ifa; /* * Create an ND6 cache for an IPv6 neighbor * that is not covered by our own prefix. */ ifa = ifaof_ifpforaddr(l3addr, ifp); if (ifa != NULL) { ifa_free(ifa); return 0; } log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", ip6_sprintf(ip6buf, &sin6->sin6_addr)); return EINVAL; } return 0; } static inline uint32_t in6_lltable_hash_dst(const struct in6_addr *dst, uint32_t hsize) { return (IN6_LLTBL_HASH(dst->s6_addr32[3], hsize)); } static uint32_t in6_lltable_hash(const struct llentry *lle, uint32_t hsize) { return (in6_lltable_hash_dst(&lle->r_l3addr.addr6, hsize)); } static void in6_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)sa; bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_addr = lle->r_l3addr.addr6; } static inline struct llentry * in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst) { struct llentry *lle; struct llentries *lleh; u_int hashidx; hashidx = in6_lltable_hash_dst(dst, llt->llt_hsize); lleh = &llt->lle_head[hashidx]; LIST_FOREACH(lle, lleh, lle_next) { if (lle->la_flags & LLE_DELETED) continue; if (IN6_ARE_ADDR_EQUAL(&lle->r_l3addr.addr6, dst)) break; } return (lle); } static void in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle) { lle->la_flags |= LLE_DELETED; EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); #ifdef DIAGNOSTIC log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif llentry_free(lle); } static struct llentry * in6_lltable_alloc(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr; struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; + char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); /* * A route that covers the given address must have * been installed 1st because we are doing a resolution, * verify this. */ if (!(flags & LLE_IFADDR) && in6_lltable_rtcheck(ifp, flags, l3addr) != 0) return (NULL); lle = in6_lltable_new(&sin6->sin6_addr, flags); if (lle == NULL) { log(LOG_INFO, "lla_lookup: new lle malloc failed\n"); return (NULL); } lle->la_flags = flags; if ((flags & LLE_IFADDR) == LLE_IFADDR) { - lltable_set_entry_addr(ifp, lle, IF_LLADDR(ifp)); + linkhdrsize = LLE_MAX_LINKHDR; + if (lltable_calc_llheader(ifp, AF_INET6, IF_LLADDR(ifp), + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return (NULL); + lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, + lladdr_off); lle->la_flags |= LLE_STATIC; } if ((lle->la_flags & LLE_STATIC) != 0) lle->ln_state = ND6_LLINFO_REACHABLE; return (lle); } static struct llentry * in6_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr; struct llentry *lle; IF_AFDATA_LOCK_ASSERT(llt->llt_ifp); KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); lle = in6_lltable_find_dst(llt, &sin6->sin6_addr); if (lle == NULL) return (NULL); KASSERT((flags & (LLE_UNLOCKED|LLE_EXCLUSIVE)) != (LLE_UNLOCKED|LLE_EXCLUSIVE),("wrong lle request flags: 0x%X", flags)); if (flags & LLE_UNLOCKED) return (lle); if (flags & LLE_EXCLUSIVE) LLE_WLOCK(lle); else LLE_RLOCK(lle); return (lle); } static int in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle, struct sysctl_req *wr) { struct ifnet *ifp = llt->llt_ifp; /* XXX stack use */ struct { struct rt_msghdr rtm; struct sockaddr_in6 sin6; /* * ndp.c assumes that sdl is word aligned */ #ifdef __LP64__ uint32_t pad; #endif struct sockaddr_dl sdl; } ndpc; struct sockaddr_dl *sdl; int error; bzero(&ndpc, sizeof(ndpc)); /* skip deleted entries */ if ((lle->la_flags & LLE_DELETED) == LLE_DELETED) return (0); /* Skip if jailed and not a valid IP of the prison. */ lltable_fill_sa_entry(lle, (struct sockaddr *)&ndpc.sin6); if (prison_if(wr->td->td_ucred, (struct sockaddr *)&ndpc.sin6) != 0) return (0); /* * produce a msg made of: * struct rt_msghdr; * struct sockaddr_in6 (IPv6) * struct sockaddr_dl; */ ndpc.rtm.rtm_msglen = sizeof(ndpc); ndpc.rtm.rtm_version = RTM_VERSION; ndpc.rtm.rtm_type = RTM_GET; ndpc.rtm.rtm_flags = RTF_UP; ndpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; if (V_deembed_scopeid) sa6_recoverscope(&ndpc.sin6); /* publish */ if (lle->la_flags & LLE_PUB) ndpc.rtm.rtm_flags |= RTF_ANNOUNCE; sdl = &ndpc.sdl; sdl->sdl_family = AF_LINK; sdl->sdl_len = sizeof(*sdl); sdl->sdl_alen = ifp->if_addrlen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); if (lle->la_expire != 0) ndpc.rtm.rtm_rmx.rmx_expire = lle->la_expire + lle->lle_remtime / hz + time_second - time_uptime; ndpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA); if (lle->la_flags & LLE_STATIC) ndpc.rtm.rtm_flags |= RTF_STATIC; if (lle->la_flags & LLE_IFADDR) ndpc.rtm.rtm_flags |= RTF_PINNED; if (lle->ln_router != 0) ndpc.rtm.rtm_flags |= RTF_GATEWAY; ndpc.rtm.rtm_rmx.rmx_pksent = lle->la_asked; /* Store state in rmx_weight value */ ndpc.rtm.rtm_rmx.rmx_state = lle->ln_state; ndpc.rtm.rtm_index = ifp->if_index; error = SYSCTL_OUT(wr, &ndpc, sizeof(ndpc)); return (error); } static struct lltable * in6_lltattach(struct ifnet *ifp) { struct lltable *llt; llt = lltable_allocate_htbl(IN6_LLTBL_DEFAULT_HSIZE); llt->llt_af = AF_INET6; llt->llt_ifp = ifp; llt->llt_lookup = in6_lltable_lookup; llt->llt_alloc_entry = in6_lltable_alloc; llt->llt_delete_entry = in6_lltable_delete_entry; llt->llt_dump_entry = in6_lltable_dump_entry; llt->llt_hash = in6_lltable_hash; llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry; llt->llt_free_entry = in6_lltable_free_entry; llt->llt_match_prefix = in6_lltable_match_prefix; lltable_link(llt); return (llt); } void * in6_domifattach(struct ifnet *ifp) { struct in6_ifextra *ext; /* There are not IPv6-capable interfaces. */ switch (ifp->if_type) { case IFT_PFLOG: case IFT_PFSYNC: case IFT_USB: return (NULL); } ext = (struct in6_ifextra *)malloc(sizeof(*ext), M_IFADDR, M_WAITOK); bzero(ext, sizeof(*ext)); ext->in6_ifstat = malloc(sizeof(counter_u64_t) * sizeof(struct in6_ifstat) / sizeof(uint64_t), M_IFADDR, M_WAITOK); COUNTER_ARRAY_ALLOC(ext->in6_ifstat, sizeof(struct in6_ifstat) / sizeof(uint64_t), M_WAITOK); ext->icmp6_ifstat = malloc(sizeof(counter_u64_t) * sizeof(struct icmp6_ifstat) / sizeof(uint64_t), M_IFADDR, M_WAITOK); COUNTER_ARRAY_ALLOC(ext->icmp6_ifstat, sizeof(struct icmp6_ifstat) / sizeof(uint64_t), M_WAITOK); ext->nd_ifinfo = nd6_ifattach(ifp); ext->scope6_id = scope6_ifattach(ifp); ext->lltable = in6_lltattach(ifp); ext->mld_ifinfo = mld_domifattach(ifp); return ext; } int in6_domifmtu(struct ifnet *ifp) { if (ifp->if_afdata[AF_INET6] == NULL) return ifp->if_mtu; return (IN6_LINKMTU(ifp)); } void in6_domifdetach(struct ifnet *ifp, void *aux) { struct in6_ifextra *ext = (struct in6_ifextra *)aux; mld_domifdetach(ifp); scope6_ifdetach(ext->scope6_id); nd6_ifdetach(ext->nd_ifinfo); lltable_free(ext->lltable); COUNTER_ARRAY_FREE(ext->in6_ifstat, sizeof(struct in6_ifstat) / sizeof(uint64_t)); free(ext->in6_ifstat, M_IFADDR); COUNTER_ARRAY_FREE(ext->icmp6_ifstat, sizeof(struct icmp6_ifstat) / sizeof(uint64_t)); free(ext->icmp6_ifstat, M_IFADDR); free(ext, M_IFADDR); } /* * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be * v4 mapped addr or v4 compat addr */ void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin, sizeof(*sin)); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = sin6->sin6_port; sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; } /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_port = sin->sin_port; sin6->sin6_addr.s6_addr32[0] = 0; sin6->sin6_addr.s6_addr32[1] = 0; sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; } /* Convert sockaddr_in6 into sockaddr_in. */ void in6_sin6_2_sin_in_sock(struct sockaddr *nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 sin6; /* * Save original sockaddr_in6 addr and convert it * to sockaddr_in. */ sin6 = *(struct sockaddr_in6 *)nam; sin_p = (struct sockaddr_in *)nam; in6_sin6_2_sin(sin_p, &sin6); } /* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 *sin6_p; sin6_p = malloc(sizeof *sin6_p, M_SONAME, M_WAITOK); sin_p = (struct sockaddr_in *)*nam; in6_sin_2_v4mapsin6(sin_p, sin6_p); free(*nam, M_SONAME); *nam = (struct sockaddr *)sin6_p; } Index: projects/clang380-import/sys/netinet6/in6.h =================================================================== --- projects/clang380-import/sys/netinet6/in6.h (revision 293015) +++ projects/clang380-import/sys/netinet6/in6.h (revision 293016) @@ -1,738 +1,738 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: in6.h,v 1.89 2001/05/27 13:28:35 itojun Exp $ */ /*- * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 * $FreeBSD$ */ #ifndef __KAME_NETINET_IN_H_INCLUDED_ #error "do not include netinet6/in6.h directly, include netinet/in.h. see RFC2553" #endif #ifndef _NETINET6_IN6_H_ #define _NETINET6_IN6_H_ /* * Identification of the network protocol stack * for *BSD-current/release: http://www.kame.net/dev/cvsweb.cgi/kame/COVERAGE * has the table of implementation/integration differences. */ #define __KAME__ #define __KAME_VERSION "FreeBSD" /* * IPv6 port allocation rules should mirror the IPv4 rules and are controlled * by the net.inet.ip.portrange sysctl tree. The following defines exist * for compatibility with userland applications that need them. */ #if __BSD_VISIBLE #define IPV6PORT_RESERVED 1024 #define IPV6PORT_ANONMIN 49152 #define IPV6PORT_ANONMAX 65535 #define IPV6PORT_RESERVEDMIN 600 #define IPV6PORT_RESERVEDMAX (IPV6PORT_RESERVED-1) #endif /* * IPv6 address */ struct in6_addr { union { uint8_t __u6_addr8[16]; uint16_t __u6_addr16[8]; uint32_t __u6_addr32[4]; } __u6_addr; /* 128-bit IP6 address */ }; #define s6_addr __u6_addr.__u6_addr8 #ifdef _KERNEL /* XXX nonstandard */ #define s6_addr8 __u6_addr.__u6_addr8 #define s6_addr16 __u6_addr.__u6_addr16 #define s6_addr32 __u6_addr.__u6_addr32 #endif #define INET6_ADDRSTRLEN 46 /* * XXX missing POSIX.1-2001 macro IPPROTO_IPV6. */ /* * Socket address for IPv6 */ #if __BSD_VISIBLE #define SIN6_LEN #endif struct sockaddr_in6 { uint8_t sin6_len; /* length of this struct */ sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* Transport layer port # */ uint32_t sin6_flowinfo; /* IP6 flow information */ struct in6_addr sin6_addr; /* IP6 address */ uint32_t sin6_scope_id; /* scope zone index */ }; /* * Local definition for masks */ #ifdef _KERNEL /* XXX nonstandard */ #define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} #define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} #define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} #define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}} #define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}} #endif #ifdef _KERNEL extern const struct sockaddr_in6 sa6_any; extern const struct in6_addr in6mask0; extern const struct in6_addr in6mask32; extern const struct in6_addr in6mask64; extern const struct in6_addr in6mask96; extern const struct in6_addr in6mask128; #endif /* _KERNEL */ /* * Macros started with IPV6_ADDR is KAME local */ #ifdef _KERNEL /* XXX nonstandard */ #if _BYTE_ORDER == _BIG_ENDIAN #define IPV6_ADDR_INT32_ONE 1 #define IPV6_ADDR_INT32_TWO 2 #define IPV6_ADDR_INT32_MNL 0xff010000 #define IPV6_ADDR_INT32_MLL 0xff020000 #define IPV6_ADDR_INT32_SMP 0x0000ffff #define IPV6_ADDR_INT16_ULL 0xfe80 #define IPV6_ADDR_INT16_USL 0xfec0 #define IPV6_ADDR_INT16_MLL 0xff02 #elif _BYTE_ORDER == _LITTLE_ENDIAN #define IPV6_ADDR_INT32_ONE 0x01000000 #define IPV6_ADDR_INT32_TWO 0x02000000 #define IPV6_ADDR_INT32_MNL 0x000001ff #define IPV6_ADDR_INT32_MLL 0x000002ff #define IPV6_ADDR_INT32_SMP 0xffff0000 #define IPV6_ADDR_INT16_ULL 0x80fe #define IPV6_ADDR_INT16_USL 0xc0fe #define IPV6_ADDR_INT16_MLL 0x02ff #endif #endif /* * Definition of some useful macros to handle IP6 addresses */ #if __BSD_VISIBLE #define IN6ADDR_ANY_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} #define IN6ADDR_LOOPBACK_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} #define IN6ADDR_NODELOCAL_ALLNODES_INIT \ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} #define IN6ADDR_INTFACELOCAL_ALLNODES_INIT \ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} #define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} #define IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16 }}} #endif extern const struct in6_addr in6addr_any; extern const struct in6_addr in6addr_loopback; #if __BSD_VISIBLE extern const struct in6_addr in6addr_nodelocal_allnodes; extern const struct in6_addr in6addr_linklocal_allnodes; extern const struct in6_addr in6addr_linklocal_allrouters; extern const struct in6_addr in6addr_linklocal_allv2routers; #endif /* * Equality * NOTE: Some of kernel programming environment (for example, openbsd/sparc) * does not supply memcmp(). For userland memcmp() is preferred as it is * in ANSI standard. */ #ifdef _KERNEL #define IN6_ARE_ADDR_EQUAL(a, b) \ (bcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #else #if __BSD_VISIBLE #define IN6_ARE_ADDR_EQUAL(a, b) \ (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #endif #endif /* * Unspecified */ #define IN6_IS_ADDR_UNSPECIFIED(a) \ ((a)->__u6_addr.__u6_addr32[0] == 0 && \ (a)->__u6_addr.__u6_addr32[1] == 0 && \ (a)->__u6_addr.__u6_addr32[2] == 0 && \ (a)->__u6_addr.__u6_addr32[3] == 0) /* * Loopback */ #define IN6_IS_ADDR_LOOPBACK(a) \ ((a)->__u6_addr.__u6_addr32[0] == 0 && \ (a)->__u6_addr.__u6_addr32[1] == 0 && \ (a)->__u6_addr.__u6_addr32[2] == 0 && \ (a)->__u6_addr.__u6_addr32[3] == ntohl(1)) /* * IPv4 compatible */ #define IN6_IS_ADDR_V4COMPAT(a) \ ((a)->__u6_addr.__u6_addr32[0] == 0 && \ (a)->__u6_addr.__u6_addr32[1] == 0 && \ (a)->__u6_addr.__u6_addr32[2] == 0 && \ (a)->__u6_addr.__u6_addr32[3] != 0 && \ (a)->__u6_addr.__u6_addr32[3] != ntohl(1)) /* * Mapped */ #define IN6_IS_ADDR_V4MAPPED(a) \ ((a)->__u6_addr.__u6_addr32[0] == 0 && \ (a)->__u6_addr.__u6_addr32[1] == 0 && \ (a)->__u6_addr.__u6_addr32[2] == ntohl(0x0000ffff)) /* * KAME Scope Values */ #ifdef _KERNEL /* XXX nonstandard */ #define IPV6_ADDR_SCOPE_NODELOCAL 0x01 #define IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 #define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 #define IPV6_ADDR_SCOPE_SITELOCAL 0x05 #define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ #define IPV6_ADDR_SCOPE_GLOBAL 0x0e #else #define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 #define __IPV6_ADDR_SCOPE_INTFACELOCAL 0x01 #define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02 #define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 #define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ #define __IPV6_ADDR_SCOPE_GLOBAL 0x0e #endif /* * Unicast Scope * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373). */ #define IN6_IS_ADDR_LINKLOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) #define IN6_IS_ADDR_SITELOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) /* * Multicast */ #define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) #ifdef _KERNEL /* XXX nonstandard */ #define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #else #define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #endif /* * Multicast Scope */ #ifdef _KERNEL /* refers nonstandard items */ #define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL)) #define IN6_IS_ADDR_MC_INTFACELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_INTFACELOCAL)) #define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL)) #define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL)) #define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL)) #define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL)) #else #define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL)) #define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL)) #define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL)) #define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL)) #define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL)) #endif #ifdef _KERNEL /* nonstandard */ /* * KAME Scope */ #define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) #define IN6_IS_SCOPE_EMBED(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_INTFACELOCAL(a))) #define IFA6_IS_DEPRECATED(a) \ ((a)->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME && \ (u_int32_t)((time_uptime - (a)->ia6_updatetime)) > \ (a)->ia6_lifetime.ia6t_pltime) #define IFA6_IS_INVALID(a) \ ((a)->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME && \ (u_int32_t)((time_uptime - (a)->ia6_updatetime)) > \ (a)->ia6_lifetime.ia6t_vltime) #endif /* _KERNEL */ /* * IP6 route structure */ #if __BSD_VISIBLE struct route_in6 { struct rtentry *ro_rt; - struct llentry *ro_lle; - struct in6_addr *ro_ia6; - int ro_flags; + char *ro_prepend; + uint16_t ro_plen; + uint16_t ro_flags; struct sockaddr_in6 ro_dst; }; #endif #ifdef _KERNEL #define MTAG_ABI_IPV6 1444287380 /* IPv6 ABI */ #define IPV6_TAG_DIRECT 0 /* direct-dispatch IPv6 */ #endif /* _KERNEL */ /* * Options for use with [gs]etsockopt at the IPV6 level. * First word of comment is data type; bool is stored in int. */ /* no hdrincl */ #if 0 /* the followings are relic in IPv4 and hence are disabled */ #define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */ #define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */ #define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */ #define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */ #define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */ #endif #define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */ #define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */ #define IPV6_MULTICAST_IF 9 /* u_int; set/get IP6 multicast i/f */ #define IPV6_MULTICAST_HOPS 10 /* int; set/get IP6 multicast hops */ #define IPV6_MULTICAST_LOOP 11 /* u_int; set/get IP6 multicast loopback */ #define IPV6_JOIN_GROUP 12 /* ipv6_mreq; join a group membership */ #define IPV6_LEAVE_GROUP 13 /* ipv6_mreq; leave a group membership */ #define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ #define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ /* RFC2292 options */ #ifdef _KERNEL #define IPV6_2292PKTINFO 19 /* bool; send/recv if, src/dst addr */ #define IPV6_2292HOPLIMIT 20 /* bool; hop limit */ #define IPV6_2292NEXTHOP 21 /* bool; next hop addr */ #define IPV6_2292HOPOPTS 22 /* bool; hop-by-hop option */ #define IPV6_2292DSTOPTS 23 /* bool; destinaion option */ #define IPV6_2292RTHDR 24 /* bool; routing header */ #define IPV6_2292PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ #endif #define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ #define IPV6_V6ONLY 27 /* bool; make AF_INET6 sockets v6 only */ #ifndef _KERNEL #define IPV6_BINDV6ONLY IPV6_V6ONLY #endif #if 1 /* IPSEC */ #define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ #endif /* IPSEC */ /* 29; unused; was IPV6_FAITH */ #if 1 /* IPV6FIREWALL */ #define IPV6_FW_ADD 30 /* add a firewall rule to chain */ #define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ #define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ #define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ #define IPV6_FW_GET 34 /* get entire firewall rule chain */ #endif /* new socket options introduced in RFC3542 */ #define IPV6_RTHDRDSTOPTS 35 /* ip6_dest; send dst option before rthdr */ #define IPV6_RECVPKTINFO 36 /* bool; recv if, dst addr */ #define IPV6_RECVHOPLIMIT 37 /* bool; recv hop limit */ #define IPV6_RECVRTHDR 38 /* bool; recv routing header */ #define IPV6_RECVHOPOPTS 39 /* bool; recv hop-by-hop option */ #define IPV6_RECVDSTOPTS 40 /* bool; recv dst option after rthdr */ #ifdef _KERNEL #define IPV6_RECVRTHDRDSTOPTS 41 /* bool; recv dst option before rthdr */ #endif #define IPV6_USE_MIN_MTU 42 /* bool; send packets at the minimum MTU */ #define IPV6_RECVPATHMTU 43 /* bool; notify an according MTU */ #define IPV6_PATHMTU 44 /* mtuinfo; get the current path MTU (sopt), 4 bytes int; MTU notification (cmsg) */ #if 0 /*obsoleted during 2292bis -> 3542*/ #define IPV6_REACHCONF 45 /* no data; ND reachability confirm (cmsg only/not in of RFC3542) */ #endif /* more new socket options introduced in RFC3542 */ #define IPV6_PKTINFO 46 /* in6_pktinfo; send if, src addr */ #define IPV6_HOPLIMIT 47 /* int; send hop limit */ #define IPV6_NEXTHOP 48 /* sockaddr; next hop addr */ #define IPV6_HOPOPTS 49 /* ip6_hbh; send hop-by-hop option */ #define IPV6_DSTOPTS 50 /* ip6_dest; send dst option befor rthdr */ #define IPV6_RTHDR 51 /* ip6_rthdr; send routing header */ #if 0 #define IPV6_PKTOPTIONS 52 /* buf/cmsghdr; set/get IPv6 options */ /* obsoleted by RFC3542 */ #endif #define IPV6_RECVTCLASS 57 /* bool; recv traffic class values */ #define IPV6_AUTOFLOWLABEL 59 /* bool; attach flowlabel automagically */ #define IPV6_TCLASS 61 /* int; send traffic class value */ #define IPV6_DONTFRAG 62 /* bool; disable IPv6 fragmentation */ #define IPV6_PREFER_TEMPADDR 63 /* int; prefer temporary addresses as * the source address. */ #define IPV6_BINDANY 64 /* bool: allow bind to any address */ #define IPV6_BINDMULTI 65 /* bool; allow multibind to same addr/port */ #define IPV6_RSS_LISTEN_BUCKET 66 /* int; set RSS listen bucket */ #define IPV6_FLOWID 67 /* int; flowid of given socket */ #define IPV6_FLOWTYPE 68 /* int; flowtype of given socket */ #define IPV6_RSSBUCKETID 69 /* int; RSS bucket ID of given socket */ #define IPV6_RECVFLOWID 70 /* bool; receive IP6 flowid/flowtype w/ datagram */ #define IPV6_RECVRSSBUCKETID 71 /* bool; receive IP6 RSS bucket id w/ datagram */ /* * The following option is private; do not use it from user applications. * It is deliberately defined to the same value as IP_MSFILTER. */ #define IPV6_MSFILTER 74 /* struct __msfilterreq; * set/get multicast source filter list. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ #define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ #define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ #define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ /* * Defaults and limits for options */ #define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */ #define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ /* * The im6o_membership vector for each socket is now dynamically allocated at * run-time, bounded by USHRT_MAX, and is reallocated when needed, sized * according to a power-of-two increment. */ #define IPV6_MIN_MEMBERSHIPS 31 #define IPV6_MAX_MEMBERSHIPS 4095 /* * Default resource limits for IPv6 multicast source filtering. * These may be modified by sysctl. */ #define IPV6_MAX_GROUP_SRC_FILTER 512 /* sources per group */ #define IPV6_MAX_SOCK_SRC_FILTER 128 /* sources per socket/group */ /* * Argument structure for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP. */ struct ipv6_mreq { struct in6_addr ipv6mr_multiaddr; unsigned int ipv6mr_interface; }; /* * IPV6_PKTINFO: Packet information(RFC2292 sec 5) */ struct in6_pktinfo { struct in6_addr ipi6_addr; /* src/dst IPv6 address */ unsigned int ipi6_ifindex; /* send/recv interface index */ }; /* * Control structure for IPV6_RECVPATHMTU socket option. */ struct ip6_mtuinfo { struct sockaddr_in6 ip6m_addr; /* or sockaddr_storage? */ uint32_t ip6m_mtu; }; /* * Argument for IPV6_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IPV6_PORTRANGE_DEFAULT 0 /* default range */ #define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ #if __BSD_VISIBLE /* * Definitions for inet6 sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ #define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */ /* * Names for IP sysctl objects */ #define IPV6CTL_FORWARDING 1 /* act as router */ #define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/ #define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */ #ifdef notyet #define IPV6CTL_DEFMTU 4 /* default MTU */ #endif #define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */ #define IPV6CTL_STATS 6 /* stats */ #define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */ #define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */ #define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */ #define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */ #define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */ #define IPV6CTL_ACCEPT_RTADV 12 /* 13; unused; was: IPV6CTL_KEEPFAITH */ #define IPV6CTL_LOG_INTERVAL 14 #define IPV6CTL_HDRNESTLIMIT 15 #define IPV6CTL_DAD_COUNT 16 #define IPV6CTL_AUTO_FLOWLABEL 17 #define IPV6CTL_DEFMCASTHLIM 18 #define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ #define IPV6CTL_KAME_VERSION 20 #define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ #define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ #if 0 /* obsolete */ #define IPV6CTL_MAPPED_ADDR 23 #endif #define IPV6CTL_V6ONLY 24 /* IPV6CTL_RTEXPIRE 25 deprecated */ /* IPV6CTL_RTMINEXPIRE 26 deprecated */ /* IPV6CTL_RTMAXCACHE 27 deprecated */ #define IPV6CTL_USETEMPADDR 32 /* use temporary addresses (RFC3041) */ #define IPV6CTL_TEMPPLTIME 33 /* preferred lifetime for tmpaddrs */ #define IPV6CTL_TEMPVLTIME 34 /* valid lifetime for tmpaddrs */ #define IPV6CTL_AUTO_LINKLOCAL 35 /* automatic link-local addr assign */ #define IPV6CTL_RIP6STATS 36 /* raw_ip6 stats */ #define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */ #define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */ #define IPV6CTL_USE_DEFAULTZONE 39 /* use default scope zone */ #define IPV6CTL_MAXFRAGS 41 /* max fragments */ #if 0 #define IPV6CTL_IFQ 42 /* ip6intrq node */ #define IPV6CTL_ISATAPRTR 43 /* isatap router */ #endif #define IPV6CTL_MCAST_PMTU 44 /* enable pMTU discovery for multicast? */ /* New entries should be added here from current IPV6CTL_MAXID value. */ /* to define items, should talk with KAME guys first, for *BSD compatibility */ #define IPV6CTL_STEALTH 45 #define ICMPV6CTL_ND6_ONLINKNSRFC4861 47 #define IPV6CTL_NO_RADR 48 /* No defroute from RA */ #define IPV6CTL_NORBIT_RAIF 49 /* Disable R-bit in NA on RA * receiving IF. */ #define IPV6CTL_RFC6204W3 50 /* Accept defroute even when forwarding enabled */ #define IPV6CTL_MAXID 51 #endif /* __BSD_VISIBLE */ /* * Since both netinet/ and netinet6/ call into netipsec/ and netpfil/, * the protocol specific mbuf flags are shared between them. */ #define M_FASTFWD_OURS M_PROTO1 /* changed dst to local */ #define M_IP6_NEXTHOP M_PROTO2 /* explicit ip nexthop */ #define M_IP_NEXTHOP M_PROTO2 /* explicit ip nexthop */ #define M_SKIP_FIREWALL M_PROTO3 /* skip firewall processing */ #define M_AUTHIPHDR M_PROTO4 #define M_DECRYPTED M_PROTO5 #define M_LOOP M_PROTO6 #define M_AUTHIPDGM M_PROTO7 #define M_RTALERT_MLD M_PROTO8 #ifdef _KERNEL struct cmsghdr; struct ip6_hdr; int in6_cksum_pseudo(struct ip6_hdr *, uint32_t, uint8_t, uint16_t); int in6_cksum(struct mbuf *, u_int8_t, u_int32_t, u_int32_t); int in6_cksum_partial(struct mbuf *, u_int8_t, u_int32_t, u_int32_t, u_int32_t); int in6_localaddr(struct in6_addr *); int in6_localip(struct in6_addr *); int in6_ifhasaddr(struct ifnet *, struct in6_addr *); int in6_addrscope(const struct in6_addr *); char *ip6_sprintf(char *, const struct in6_addr *); struct in6_ifaddr *in6_ifawithifp(struct ifnet *, struct in6_addr *); extern void in6_if_up(struct ifnet *); struct sockaddr; extern u_char ip6_protox[]; void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6); void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6); void in6_sin6_2_sin_in_sock(struct sockaddr *nam); void in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam); extern void addrsel_policy_init(void); #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) #endif /* _KERNEL */ #ifndef _SIZE_T_DECLARED typedef __size_t size_t; #define _SIZE_T_DECLARED #endif #ifndef _SOCKLEN_T_DECLARED typedef __socklen_t socklen_t; #define _SOCKLEN_T_DECLARED #endif #if __BSD_VISIBLE __BEGIN_DECLS struct cmsghdr; extern int inet6_option_space(int); extern int inet6_option_init(void *, struct cmsghdr **, int); extern int inet6_option_append(struct cmsghdr *, const uint8_t *, int, int); extern uint8_t *inet6_option_alloc(struct cmsghdr *, int, int, int); extern int inet6_option_next(const struct cmsghdr *, uint8_t **); extern int inet6_option_find(const struct cmsghdr *, uint8_t **, int); extern size_t inet6_rthdr_space(int, int); extern struct cmsghdr *inet6_rthdr_init(void *, int); extern int inet6_rthdr_add(struct cmsghdr *, const struct in6_addr *, unsigned int); extern int inet6_rthdr_lasthop(struct cmsghdr *, unsigned int); #if 0 /* not implemented yet */ extern int inet6_rthdr_reverse(const struct cmsghdr *, struct cmsghdr *); #endif extern int inet6_rthdr_segments(const struct cmsghdr *); extern struct in6_addr *inet6_rthdr_getaddr(struct cmsghdr *, int); extern int inet6_rthdr_getflags(const struct cmsghdr *, int); extern int inet6_opt_init(void *, socklen_t); extern int inet6_opt_append(void *, socklen_t, int, uint8_t, socklen_t, uint8_t, void **); extern int inet6_opt_finish(void *, socklen_t, int); extern int inet6_opt_set_val(void *, int, void *, socklen_t); extern int inet6_opt_next(void *, socklen_t, int, uint8_t *, socklen_t *, void **); extern int inet6_opt_find(void *, socklen_t, int, uint8_t, socklen_t *, void **); extern int inet6_opt_get_val(void *, int, void *, socklen_t); extern socklen_t inet6_rth_space(int, int); extern void *inet6_rth_init(void *, socklen_t, int, int); extern int inet6_rth_add(void *, const struct in6_addr *); extern int inet6_rth_reverse(const void *, void *); extern int inet6_rth_segments(const void *); extern struct in6_addr *inet6_rth_getaddr(const void *, int); __END_DECLS #endif /* __BSD_VISIBLE */ #endif /* !_NETINET6_IN6_H_ */ Index: projects/clang380-import/sys/netinet6/ip6_output.c =================================================================== --- projects/clang380-import/sys/netinet6/ip6_output.c (revision 293015) +++ projects/clang380-import/sys/netinet6/ip6_output.c (revision 293016) @@ -1,2997 +1,2998 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $ */ /*- * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipfw.h" #include "opt_ipsec.h" #include "opt_sctp.h" #include "opt_route.h" #include "opt_rss.h" #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 #include #include #include #include #ifdef IPSEC #include #include #include #include #endif /* IPSEC */ #ifdef SCTP #include #include #endif #include #include #ifdef FLOWTABLE #include #endif extern int in6_mcast_loop; struct ip6_exthdrs { struct mbuf *ip6e_ip6; struct mbuf *ip6e_hbh; struct mbuf *ip6e_dest1; struct mbuf *ip6e_rthdr; struct mbuf *ip6e_dest2; }; static int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **, struct ucred *, int); static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *, struct socket *, struct sockopt *); static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, struct ucred *, int, int, int); static int ip6_copyexthdr(struct mbuf **, caddr_t, int); static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, struct ip6_frag **); static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); static int ip6_getpmtu(struct route_in6 *, struct route_in6 *, struct ifnet *, struct in6_addr *, u_long *, int *, u_int); static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); /* * Make an extension header from option data. hp is the source, and * mp is the destination. */ #define MAKE_EXTHDR(hp, mp) \ do { \ if (hp) { \ struct ip6_ext *eh = (struct ip6_ext *)(hp); \ error = ip6_copyexthdr((mp), (caddr_t)(hp), \ ((eh)->ip6e_len + 1) << 3); \ if (error) \ goto freehdrs; \ } \ } while (/*CONSTCOND*/ 0) /* * Form a chain of extension headers. * m is the extension header mbuf * mp is the previous mbuf in the chain * p is the next header * i is the type of option. */ #define MAKE_CHAIN(m, mp, p, i)\ do {\ if (m) {\ if (!hdrsplit) \ panic("assumption failed: hdr not split"); \ *mtod((m), u_char *) = *(p);\ *(p) = (i);\ p = mtod((m), u_char *);\ (m)->m_next = (mp)->m_next;\ (mp)->m_next = (m);\ (mp) = (m);\ }\ } while (/*CONSTCOND*/ 0) void in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset) { u_short csum; csum = in_cksum_skip(m, offset + plen, offset); if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6 && csum == 0) csum = 0xffff; offset += m->m_pkthdr.csum_data; /* checksum offset */ if (offset + sizeof(u_short) > m->m_len) { printf("%s: delayed m_pullup, m->len: %d plen %u off %u " "csum_flags=%b\n", __func__, m->m_len, plen, offset, (int)m->m_pkthdr.csum_flags, CSUM_BITS); /* * XXX this should not happen, but if it does, the correct * behavior may be to insert the checksum in the appropriate * next mbuf in the chain. */ return; } *(u_short *)(m->m_data + offset) = csum; } int ip6_fragment(struct ifnet *ifp, struct mbuf *m0, int hlen, u_char nextproto, int mtu, uint32_t id) { struct mbuf *m, **mnext, *m_frgpart; struct ip6_hdr *ip6, *mhip6; struct ip6_frag *ip6f; int off; int error; int tlen = m0->m_pkthdr.len; m = m0; ip6 = mtod(m, struct ip6_hdr *); mnext = &m->m_nextpkt; for (off = hlen; off < tlen; off += mtu) { m = m_gethdr(M_NOWAIT, MT_DATA); if (!m) { IP6STAT_INC(ip6s_odropped); return (ENOBUFS); } m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; m->m_data += max_linkhdr; mhip6 = mtod(m, struct ip6_hdr *); *mhip6 = *ip6; m->m_len = sizeof(*mhip6); error = ip6_insertfraghdr(m0, m, hlen, &ip6f); if (error) { IP6STAT_INC(ip6s_odropped); return (error); } ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7)); if (off + mtu >= tlen) mtu = tlen - off; else ip6f->ip6f_offlg |= IP6F_MORE_FRAG; mhip6->ip6_plen = htons((u_short)(mtu + hlen + sizeof(*ip6f) - sizeof(struct ip6_hdr))); if ((m_frgpart = m_copy(m0, off, mtu)) == 0) { IP6STAT_INC(ip6s_odropped); return (ENOBUFS); } m_cat(m, m_frgpart); m->m_pkthdr.len = mtu + hlen + sizeof(*ip6f); m->m_pkthdr.fibnum = m0->m_pkthdr.fibnum; m->m_pkthdr.rcvif = NULL; ip6f->ip6f_reserved = 0; ip6f->ip6f_ident = id; ip6f->ip6f_nxt = nextproto; IP6STAT_INC(ip6s_ofragments); in6_ifstat_inc(ifp, ifs6_out_fragcreat); } return (0); } /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 * header (with pri, len, nxt, hlim, src, dst). * This function may modify ver and hlim only. * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. * If route_in6 ro is present and has ro_rt initialized, route lookup would be * skipped and ro->ro_rt would be used. If ro is present but ro->ro_rt is NULL, * then result of route lookup is stored in ro->ro_rt. * * type of "mtu": rt_mtu is u_long, ifnet.ifr_mtu is int, and * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, * which is rt_mtu. * * ifpp - XXX: just for statistics */ /* * XXX TODO: no flowid is assigned for outbound flows? */ int ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro, int flags, struct ip6_moptions *im6o, struct ifnet **ifpp, struct inpcb *inp) { struct ip6_hdr *ip6; struct ifnet *ifp, *origifp; struct mbuf *m = m0; struct mbuf *mprev = NULL; int hlen, tlen, len; struct route_in6 ip6route; struct rtentry *rt = NULL; struct sockaddr_in6 *dst, src_sa, dst_sa; struct in6_addr odst; int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; int alwaysfrag, dontfrag; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; struct in6_addr finaldst, src0, dst0; u_int32_t zone; struct route_in6 *ro_pmtu = NULL; int hdrsplit = 0; int sw_csum, tso; int needfiblookup; uint32_t fibnum; struct m_tag *fwd_tag = NULL; uint32_t id; ip6 = mtod(m, struct ip6_hdr *); if (ip6 == NULL) { printf ("ip6 is NULL"); goto bad; } if (inp != NULL) { M_SETFIB(m, inp->inp_inc.inc_fibnum); if ((flags & IP_NODEFAULTFLOWID) == 0) { /* unconditionally set flowid */ m->m_pkthdr.flowid = inp->inp_flowid; M_HASHTYPE_SET(m, inp->inp_flowtype); } } finaldst = ip6->ip6_dst; bzero(&exthdrs, sizeof(exthdrs)); if (opt) { /* Hop-by-Hop options header */ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); /* Destination options header(1st part) */ if (opt->ip6po_rthdr) { /* * Destination options header(1st part) * This only makes sense with a routing header. * See Section 9.2 of RFC 3542. * Disabling this part just for MIP6 convenience is * a bad idea. We need to think carefully about a * way to make the advanced API coexist with MIP6 * options, which might automatically be inserted in * the kernel. */ MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); } /* Routing header */ MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); /* Destination options header(2nd part) */ MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); } #ifdef IPSEC /* * IPSec checking which handles several cases. * FAST IPSEC: We re-injected the packet. * XXX: need scope argument. */ switch(ip6_ipsec_output(&m, inp, &error)) { case 1: /* Bad packet */ goto freehdrs; case -1: /* IPSec done */ goto done; case 0: /* No IPSec */ default: break; } #endif /* IPSEC */ /* * Calculate the total length of the extension header chain. * Keep the length of the unfragmentable part for fragmentation. */ optlen = 0; if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len; if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len; if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len; unfragpartlen = optlen + sizeof(struct ip6_hdr); /* NOTE: we don't add AH/ESP length here (done in ip6_ipsec_output) */ if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len; /* * If there is at least one extension header, * separate IP6 header from the payload. */ if (optlen && !hdrsplit) { if ((error = ip6_splithdr(m, &exthdrs)) != 0) { m = NULL; goto freehdrs; } m = exthdrs.ip6e_ip6; hdrsplit++; } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); /* adjust mbuf packet header length */ m->m_pkthdr.len += optlen; plen = m->m_pkthdr.len - sizeof(*ip6); /* If this is a jumbo payload, insert a jumbo payload option. */ if (plen > IPV6_MAXPACKET) { if (!hdrsplit) { if ((error = ip6_splithdr(m, &exthdrs)) != 0) { m = NULL; goto freehdrs; } m = exthdrs.ip6e_ip6; hdrsplit++; } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) goto freehdrs; ip6->ip6_plen = 0; } else ip6->ip6_plen = htons(plen); /* * Concatenate headers and fill in next header fields. * Here we have, on "m" * IPv6 payload * and we insert headers accordingly. Finally, we should be getting: * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] * * during the header composing process, "m" points to IPv6 header. * "mprev" points to an extension header prior to esp. */ u_char *nexthdrp = &ip6->ip6_nxt; mprev = m; /* * we treat dest2 specially. this makes IPsec processing * much easier. the goal here is to make mprev point the * mbuf prior to dest2. * * result: IPv6 dest2 payload * m and mprev will point to IPv6 header. */ if (exthdrs.ip6e_dest2) { if (!hdrsplit) panic("assumption failed: hdr not split"); exthdrs.ip6e_dest2->m_next = m->m_next; m->m_next = exthdrs.ip6e_dest2; *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; ip6->ip6_nxt = IPPROTO_DSTOPTS; } /* * result: IPv6 hbh dest1 rthdr dest2 payload * m will point to IPv6 header. mprev will point to the * extension header prior to dest2 (rthdr in the above case). */ MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS); MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp, IPPROTO_DSTOPTS); MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, IPPROTO_ROUTING); /* * If there is a routing header, discard the packet. */ if (exthdrs.ip6e_rthdr) { error = EINVAL; goto bad; } /* Source address validation */ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && (flags & IPV6_UNSPECSRC) == 0) { error = EOPNOTSUPP; IP6STAT_INC(ip6s_badscope); goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { error = EOPNOTSUPP; IP6STAT_INC(ip6s_badscope); goto bad; } IP6STAT_INC(ip6s_localout); /* * Route packet. */ if (ro == 0) { ro = &ip6route; bzero((caddr_t)ro, sizeof(*ro)); } ro_pmtu = ro; if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; #ifdef FLOWTABLE if (ro->ro_rt == NULL) (void )flowtable_lookup(AF_INET6, m, (struct route *)ro); #endif fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); again: /* * if specified, try to fill in the traffic class field. * do not override if a non-zero value is already set. * we check the diffserv field and the ecn field separately. */ if (opt && opt->ip6po_tclass >= 0) { int mask = 0; if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0) mask |= 0xfc; if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0) mask |= 0x03; if (mask != 0) ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20); } /* fill in or override the hop limit field, if necessary. */ if (opt && opt->ip6po_hlim != -1) ip6->ip6_hlim = opt->ip6po_hlim & 0xff; else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (im6o != NULL) ip6->ip6_hlim = im6o->im6o_multicast_hlim; else ip6->ip6_hlim = V_ip6_defmcasthlim; } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); if (ro->ro_rt && fwd_tag == NULL) { rt = ro->ro_rt; ifp = ro->ro_rt->rt_ifp; } else { if (fwd_tag == NULL) { bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; } error = in6_selectroute_fib(&dst_sa, opt, im6o, ro, &ifp, &rt, fibnum); if (error != 0) { if (ifp != NULL) in6_ifstat_inc(ifp, ifs6_out_discard); goto bad; } } if (rt == NULL) { /* * If in6_selectroute() does not return a route entry, * dst may not have been updated. */ *dst = dst_sa; /* XXX */ } /* * then rt (for unicast) and ifp must be non-NULL valid values. */ if ((flags & IPV6_FORWARDING) == 0) { /* XXX: the FORWARDING flag can be set for mrouting. */ in6_ifstat_inc(ifp, ifs6_out_request); } if (rt != NULL) { ia = (struct in6_ifaddr *)(rt->rt_ifa); counter_u64_add(rt->rt_pksent, 1); } /* * The outgoing interface must be in the zone of source and * destination addresses. */ origifp = ifp; src0 = ip6->ip6_src; if (in6_setscope(&src0, origifp, &zone)) goto badscope; bzero(&src_sa, sizeof(src_sa)); src_sa.sin6_family = AF_INET6; src_sa.sin6_len = sizeof(src_sa); src_sa.sin6_addr = ip6->ip6_src; if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id) goto badscope; dst0 = ip6->ip6_dst; if (in6_setscope(&dst0, origifp, &zone)) goto badscope; /* re-initialize to be sure */ bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) { goto badscope; } /* We should use ia_ifp to support the case of * sending packets to an address of our own. */ if (ia != NULL && ia->ia_ifp) ifp = ia->ia_ifp; /* scope check is done. */ goto routefound; badscope: IP6STAT_INC(ip6s_badscope); in6_ifstat_inc(origifp, ifs6_out_discard); if (error == 0) error = EHOSTUNREACH; /* XXX */ goto bad; routefound: if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (opt && opt->ip6po_nextroute.ro_rt) { /* * The nexthop is explicitly specified by the * application. We assume the next hop is an IPv6 * address. */ dst = (struct sockaddr_in6 *)opt->ip6po_nexthop; } else if ((rt->rt_flags & RTF_GATEWAY)) dst = (struct sockaddr_in6 *)rt->rt_gateway; } if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ } else { m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; in6_ifstat_inc(ifp, ifs6_out_mcast); /* * Confirm that the outgoing interface supports multicast. */ if (!(ifp->if_flags & IFF_MULTICAST)) { IP6STAT_INC(ip6s_noroute); in6_ifstat_inc(ifp, ifs6_out_discard); error = ENETUNREACH; goto bad; } if ((im6o == NULL && in6_mcast_loop) || (im6o && im6o->im6o_multicast_loop)) { /* * Loop back multicast datagram if not expressly * forbidden to do so, even if we have not joined * the address; protocols will filter it later, * thus deferring a hash lookup and lock acquisition * at the expense of an m_copym(). */ ip6_mloopback(ifp, m); } else { /* * If we are acting as a multicast router, perform * multicast forwarding as if the packet had just * arrived on the interface to which we are about * to send. The multicast forwarding function * recursively calls this function, using the * IPV6_FORWARDING flag to prevent infinite recursion. * * Multicasts that are looped back by ip6_mloopback(), * above, will be forwarded by the ip6_input() routine, * if necessary. */ if (V_ip6_mrouter && (flags & IPV6_FORWARDING) == 0) { /* * XXX: ip6_mforward expects that rcvif is NULL * when it is called from the originating path. * However, it may not always be the case. */ m->m_pkthdr.rcvif = NULL; if (ip6_mforward(ip6, ifp, m) != 0) { m_freem(m); goto done; } } } /* * Multicasts with a hoplimit of zero may be looped back, * above, but must not be transmitted on a network. * Also, multicasts addressed to the loopback interface * are not sent -- the above call to ip6_mloopback() will * loop back a copy if this host actually belongs to the * destination group on the loopback interface. */ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) || IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) { m_freem(m); goto done; } } /* * Fill the outgoing inteface to tell the upper layer * to increment per-interface statistics. */ if (ifpp) *ifpp = ifp; /* Determine path MTU. */ if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, &alwaysfrag, fibnum)) != 0) goto bad; /* * The caller of this function may specify to use the minimum MTU * in some cases. * An advanced API option (IPV6_USE_MIN_MTU) can also override MTU * setting. The logic is a bit complicated; by default, unicast * packets will follow path MTU while multicast packets will be sent at * the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets * including unicast ones will be sent at the minimum MTU. Multicast * packets will always be sent at the minimum MTU unless * IP6PO_MINMTU_DISABLE is explicitly specified. * See RFC 3542 for more details. */ if (mtu > IPV6_MMTU) { if ((flags & IPV6_MINMTU)) mtu = IPV6_MMTU; else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL) mtu = IPV6_MMTU; else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && (opt == NULL || opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) { mtu = IPV6_MMTU; } } /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* * If the outgoing packet contains a hop-by-hop options header, * it must be examined and processed even by the source node. * (RFC 2460, section 4.) */ if (exthdrs.ip6e_hbh) { struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); u_int32_t dummy; /* XXX unused */ u_int32_t plen = 0; /* XXX: ip6_process will check the value */ #ifdef DIAGNOSTIC if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) panic("ip6e_hbh is not contiguous"); #endif /* * XXX: if we have to send an ICMPv6 error to the sender, * we need the M_LOOP flag since icmp6_error() expects * the IPv6 and the hop-by-hop options header are * contiguous unless the flag is set. */ m->m_flags |= M_LOOP; m->m_pkthdr.rcvif = ifp; if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1), ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), &dummy, &plen) < 0) { /* m was already freed at this point */ error = EINVAL;/* better error? */ goto done; } m->m_flags &= ~M_LOOP; /* XXX */ m->m_pkthdr.rcvif = NULL; } /* Jump over all PFIL processing if hooks are not active. */ if (!PFIL_HOOKED(&V_inet6_pfil_hook)) goto passout; odst = ip6->ip6_dst; /* Run through list of hooks for output packets. */ error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, inp); if (error != 0 || m == NULL) goto done; ip6 = mtod(m, struct ip6_hdr *); needfiblookup = 0; /* See if destination IP address was changed by packet filter. */ if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) { m->m_flags |= M_SKIP_FIREWALL; /* If destination is now ourself drop to ip6_input(). */ if (in6_localip(&ip6->ip6_dst)) { m->m_flags |= M_FASTFWD_OURS; if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif error = netisr_queue(NETISR_IPV6, m); goto done; } else needfiblookup = 1; /* Redo the routing table lookup. */ } /* See if fib was changed by packet filter. */ if (fibnum != M_GETFIB(m)) { m->m_flags |= M_SKIP_FIREWALL; fibnum = M_GETFIB(m); RO_RTFREE(ro); needfiblookup = 1; } if (needfiblookup) goto again; /* See if local, if yes, send it to netisr. */ if (m->m_flags & M_FASTFWD_OURS) { if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = V_loif; if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; #endif error = netisr_queue(NETISR_IPV6, m); goto done; } /* Or forward to some other address? */ if ((m->m_flags & M_IP6_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { dst = (struct sockaddr_in6 *)&ro->ro_dst; bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6)); m->m_flags |= M_SKIP_FIREWALL; m->m_flags &= ~M_IP6_NEXTHOP; m_tag_delete(m, fwd_tag); goto again; } passout: /* * Send the packet to the outgoing interface. * If necessary, do IPv6 fragmentation before sending. * * the logic here is rather complex: * 1: normal case (dontfrag == 0, alwaysfrag == 0) * 1-a: send as is if tlen <= path mtu * 1-b: fragment if tlen > path mtu * * 2: if user asks us not to fragment (dontfrag == 1) * 2-a: send as is if tlen <= interface mtu * 2-b: error if tlen > interface mtu * * 3: if we always need to attach fragment header (alwaysfrag == 1) * always fragment * * 4: if dontfrag == 1 && alwaysfrag == 1 * error, as we cannot handle this conflicting request */ sw_csum = m->m_pkthdr.csum_flags; if (!hdrsplit) { tso = ((sw_csum & ifp->if_hwassist & CSUM_TSO) != 0) ? 1 : 0; sw_csum &= ~ifp->if_hwassist; } else tso = 0; /* * If we added extension headers, we will not do TSO and calculate the * checksums ourselves for now. * XXX-BZ Need a framework to know when the NIC can handle it, even * with ext. hdrs. */ if (sw_csum & CSUM_DELAY_DATA_IPV6) { sw_csum &= ~CSUM_DELAY_DATA_IPV6; in6_delayed_cksum(m, plen, sizeof(struct ip6_hdr)); } #ifdef SCTP if (sw_csum & CSUM_SCTP_IPV6) { sw_csum &= ~CSUM_SCTP_IPV6; sctp_delayed_cksum(m, sizeof(struct ip6_hdr)); } #endif m->m_pkthdr.csum_flags &= ifp->if_hwassist; tlen = m->m_pkthdr.len; if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso) dontfrag = 1; else dontfrag = 0; if (dontfrag && alwaysfrag) { /* case 4 */ /* conflicting request - can't transmit */ error = EMSGSIZE; goto bad; } if (dontfrag && tlen > IN6_LINKMTU(ifp) && !tso) { /* case 2-b */ /* * Even if the DONTFRAG option is specified, we cannot send the * packet when the data length is larger than the MTU of the * outgoing interface. * Notify the error by sending IPV6_PATHMTU ancillary data if * application wanted to know the MTU value. Also return an * error code (this is not described in the API spec). */ if (inp != NULL) ip6_notify_pmtu(inp, &dst_sa, (u_int32_t)mtu); error = EMSGSIZE; goto bad; } /* * transmit packet without fragmentation */ if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */ struct in6_ifaddr *ia6; ip6 = mtod(m, struct ip6_hdr *); ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); if (ia6) { /* Record statistics for this interface address. */ counter_u64_add(ia6->ia_ifa.ifa_opackets, 1); counter_u64_add(ia6->ia_ifa.ifa_obytes, m->m_pkthdr.len); ifa_free(&ia6->ia_ifa); } error = nd6_output_ifp(ifp, origifp, m, dst, NULL); goto done; } /* * try to fragment the packet. case 1-b and 3 */ if (mtu < IPV6_MMTU) { /* path MTU cannot be less than IPV6_MMTU */ error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */ error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } else { u_char nextproto; /* * Too large for the destination or interface; * fragment if possible. * Must be able to put at least 8 bytes per fragment. */ hlen = unfragpartlen; if (mtu > IPV6_MAXPACKET) mtu = IPV6_MAXPACKET; len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } /* * If the interface will not calculate checksums on * fragmented packets, then do it here. * XXX-BZ handle the hw offloading case. Need flags. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { in6_delayed_cksum(m, plen, hlen); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; } #ifdef SCTP if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) { sctp_delayed_cksum(m, hlen); m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6; } #endif /* * Change the next header field of the last header in the * unfragmentable part. */ if (exthdrs.ip6e_rthdr) { nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; } else if (exthdrs.ip6e_dest1) { nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; } else if (exthdrs.ip6e_hbh) { nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; } else { nextproto = ip6->ip6_nxt; ip6->ip6_nxt = IPPROTO_FRAGMENT; } /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto * chain. */ m0 = m; id = htonl(ip6_randomid()); if ((error = ip6_fragment(ifp, m, hlen, nextproto, len, id))) goto sendorfree; in6_ifstat_inc(ifp, ifs6_out_fragok); } /* * Remove leading garbages. */ sendorfree: m = m0->m_nextpkt; m0->m_nextpkt = 0; m_freem(m0); for (m0 = m; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { /* Record statistics for this interface address. */ if (ia) { counter_u64_add(ia->ia_ifa.ifa_opackets, 1); counter_u64_add(ia->ia_ifa.ifa_obytes, m->m_pkthdr.len); } error = nd6_output_ifp(ifp, origifp, m, dst, NULL); } else m_freem(m); } if (error == 0) IP6STAT_INC(ip6s_fragmented); done: if (ro == &ip6route) RO_RTFREE(ro); if (ro_pmtu == &ip6route) RO_RTFREE(ro_pmtu); return (error); freehdrs: m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ m_freem(exthdrs.ip6e_dest1); m_freem(exthdrs.ip6e_rthdr); m_freem(exthdrs.ip6e_dest2); /* FALLTHROUGH */ bad: if (m) m_freem(m); goto done; } static int ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen) { struct mbuf *m; if (hlen > MCLBYTES) return (ENOBUFS); /* XXX */ if (hlen > MLEN) m = m_getcl(M_NOWAIT, MT_DATA, 0); else m = m_get(M_NOWAIT, MT_DATA); if (m == NULL) return (ENOBUFS); m->m_len = hlen; if (hdr) bcopy(hdr, mtod(m, caddr_t), hlen); *mp = m; return (0); } /* * Insert jumbo payload option. */ static int ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen) { struct mbuf *mopt; u_char *optbuf; u_int32_t v; #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ /* * If there is no hop-by-hop options header, allocate new one. * If there is one but it doesn't have enough space to store the * jumbo payload option, allocate a cluster to store the whole options. * Otherwise, use it to store the options. */ if (exthdrs->ip6e_hbh == 0) { mopt = m_get(M_NOWAIT, MT_DATA); if (mopt == NULL) return (ENOBUFS); mopt->m_len = JUMBOOPTLEN; optbuf = mtod(mopt, u_char *); optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ exthdrs->ip6e_hbh = mopt; } else { struct ip6_hbh *hbh; mopt = exthdrs->ip6e_hbh; if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { /* * XXX assumption: * - exthdrs->ip6e_hbh is not referenced from places * other than exthdrs. * - exthdrs->ip6e_hbh is not an mbuf chain. */ int oldoptlen = mopt->m_len; struct mbuf *n; /* * XXX: give up if the whole (new) hbh header does * not fit even in an mbuf cluster. */ if (oldoptlen + JUMBOOPTLEN > MCLBYTES) return (ENOBUFS); /* * As a consequence, we must always prepare a cluster * at this point. */ n = m_getcl(M_NOWAIT, MT_DATA, 0); if (n == NULL) return (ENOBUFS); n->m_len = oldoptlen + JUMBOOPTLEN; bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t), oldoptlen); optbuf = mtod(n, caddr_t) + oldoptlen; m_freem(mopt); mopt = exthdrs->ip6e_hbh = n; } else { optbuf = mtod(mopt, u_char *) + mopt->m_len; mopt->m_len += JUMBOOPTLEN; } optbuf[0] = IP6OPT_PADN; optbuf[1] = 1; /* * Adjust the header length according to the pad and * the jumbo payload option. */ hbh = mtod(mopt, struct ip6_hbh *); hbh->ip6h_len += (JUMBOOPTLEN >> 3); } /* fill in the option. */ optbuf[2] = IP6OPT_JUMBO; optbuf[3] = 4; v = (u_int32_t)htonl(plen + JUMBOOPTLEN); bcopy(&v, &optbuf[4], sizeof(u_int32_t)); /* finally, adjust the packet header length */ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; return (0); #undef JUMBOOPTLEN } /* * Insert fragment header and copy unfragmentable header portions. */ static int ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, struct ip6_frag **frghdrp) { struct mbuf *n, *mlast; if (hlen > sizeof(struct ip6_hdr)) { n = m_copym(m0, sizeof(struct ip6_hdr), hlen - sizeof(struct ip6_hdr), M_NOWAIT); if (n == 0) return (ENOBUFS); m->m_next = n; } else n = m; /* Search for the last mbuf of unfragmentable part. */ for (mlast = n; mlast->m_next; mlast = mlast->m_next) ; if (M_WRITABLE(mlast) && M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) { /* use the trailing space of the last mbuf for the fragment hdr */ *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); mlast->m_len += sizeof(struct ip6_frag); m->m_pkthdr.len += sizeof(struct ip6_frag); } else { /* allocate a new mbuf for the fragment header */ struct mbuf *mfrg; mfrg = m_get(M_NOWAIT, MT_DATA); if (mfrg == NULL) return (ENOBUFS); mfrg->m_len = sizeof(struct ip6_frag); *frghdrp = mtod(mfrg, struct ip6_frag *); mlast->m_next = mfrg; } return (0); } static int ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, struct ifnet *ifp, struct in6_addr *dst, u_long *mtup, int *alwaysfragp, u_int fibnum) { u_int32_t mtu = 0; int alwaysfrag = 0; int error = 0; if (ro_pmtu != ro) { /* The first hop and the final destination may differ. */ struct sockaddr_in6 *sa6_dst = (struct sockaddr_in6 *)&ro_pmtu->ro_dst; if (ro_pmtu->ro_rt && ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) { RTFREE(ro_pmtu->ro_rt); ro_pmtu->ro_rt = (struct rtentry *)NULL; } if (ro_pmtu->ro_rt == NULL) { bzero(sa6_dst, sizeof(*sa6_dst)); sa6_dst->sin6_family = AF_INET6; sa6_dst->sin6_len = sizeof(struct sockaddr_in6); sa6_dst->sin6_addr = *dst; in6_rtalloc(ro_pmtu, fibnum); } } if (ro_pmtu->ro_rt) { u_int32_t ifmtu; struct in_conninfo inc; bzero(&inc, sizeof(inc)); inc.inc_flags |= INC_ISIPV6; inc.inc6_faddr = *dst; if (ifp == NULL) ifp = ro_pmtu->ro_rt->rt_ifp; ifmtu = IN6_LINKMTU(ifp); mtu = tcp_hc_getmtu(&inc); if (mtu) mtu = min(mtu, ro_pmtu->ro_rt->rt_mtu); else mtu = ro_pmtu->ro_rt->rt_mtu; if (mtu == 0) mtu = ifmtu; else if (mtu < IPV6_MMTU) { /* * RFC2460 section 5, last paragraph: * if we record ICMPv6 too big message with * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU * or smaller, with framgent header attached. * (fragment header is needed regardless from the * packet size, for translators to identify packets) */ alwaysfrag = 1; mtu = IPV6_MMTU; } } else if (ifp) { mtu = IN6_LINKMTU(ifp); } else error = EHOSTUNREACH; /* XXX */ *mtup = mtu; if (alwaysfragp) *alwaysfragp = alwaysfrag; return (error); } /* * IP6 socket option processing. */ int ip6_ctloutput(struct socket *so, struct sockopt *sopt) { int optdatalen, uproto; void *optdata; struct inpcb *in6p = sotoinpcb(so); int error, optval; int level, op, optname; int optlen; struct thread *td; #ifdef RSS uint32_t rss_bucket; int retval; #endif level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; td = sopt->sopt_td; error = 0; optval = 0; uproto = (int)so->so_proto->pr_protocol; if (level != IPPROTO_IPV6) { error = EINVAL; if (sopt->sopt_level == SOL_SOCKET && sopt->sopt_dir == SOPT_SET) { switch (sopt->sopt_name) { case SO_REUSEADDR: INP_WLOCK(in6p); if ((so->so_options & SO_REUSEADDR) != 0) in6p->inp_flags2 |= INP_REUSEADDR; else in6p->inp_flags2 &= ~INP_REUSEADDR; INP_WUNLOCK(in6p); error = 0; break; case SO_REUSEPORT: INP_WLOCK(in6p); if ((so->so_options & SO_REUSEPORT) != 0) in6p->inp_flags2 |= INP_REUSEPORT; else in6p->inp_flags2 &= ~INP_REUSEPORT; INP_WUNLOCK(in6p); error = 0; break; case SO_SETFIB: INP_WLOCK(in6p); in6p->inp_inc.inc_fibnum = so->so_fibnum; INP_WUNLOCK(in6p); error = 0; break; default: break; } } } else { /* level == IPPROTO_IPV6 */ switch (op) { case SOPT_SET: switch (optname) { case IPV6_2292PKTOPTIONS: #ifdef IPV6_PKTOPTIONS case IPV6_PKTOPTIONS: #endif { struct mbuf *m; error = soopt_getm(sopt, &m); /* XXX */ if (error != 0) break; error = soopt_mcopyin(sopt, m); /* XXX */ if (error != 0) break; error = ip6_pcbopts(&in6p->in6p_outputopts, m, so, sopt); m_freem(m); /* XXX */ break; } /* * Use of some Hop-by-Hop options or some * Destination options, might require special * privilege. That is, normal applications * (without special privilege) might be forbidden * from setting certain options in outgoing packets, * and might never see certain options in received * packets. [RFC 2292 Section 6] * KAME specific note: * KAME prevents non-privileged users from sending or * receiving ANY hbh/dst options in order to avoid * overhead of parsing options in the kernel. */ case IPV6_RECVHOPOPTS: case IPV6_RECVDSTOPTS: case IPV6_RECVRTHDRDSTOPTS: if (td != NULL) { error = priv_check(td, PRIV_NETINET_SETHDROPTS); if (error) break; } /* FALLTHROUGH */ case IPV6_UNICAST_HOPS: case IPV6_HOPLIMIT: case IPV6_RECVPKTINFO: case IPV6_RECVHOPLIMIT: case IPV6_RECVRTHDR: case IPV6_RECVPATHMTU: case IPV6_RECVTCLASS: case IPV6_RECVFLOWID: #ifdef RSS case IPV6_RECVRSSBUCKETID: #endif case IPV6_V6ONLY: case IPV6_AUTOFLOWLABEL: case IPV6_BINDANY: case IPV6_BINDMULTI: #ifdef RSS case IPV6_RSS_LISTEN_BUCKET: #endif if (optname == IPV6_BINDANY && td != NULL) { error = priv_check(td, PRIV_NETINET_BINDANY); if (error) break; } if (optlen != sizeof(int)) { error = EINVAL; break; } error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (optname) { case IPV6_UNICAST_HOPS: if (optval < -1 || optval >= 256) error = EINVAL; else { /* -1 = kernel default */ in6p->in6p_hops = optval; if ((in6p->inp_vflag & INP_IPV4) != 0) in6p->inp_ip_ttl = optval; } break; #define OPTSET(bit) \ do { \ INP_WLOCK(in6p); \ if (optval) \ in6p->inp_flags |= (bit); \ else \ in6p->inp_flags &= ~(bit); \ INP_WUNLOCK(in6p); \ } while (/*CONSTCOND*/ 0) #define OPTSET2292(bit) \ do { \ INP_WLOCK(in6p); \ in6p->inp_flags |= IN6P_RFC2292; \ if (optval) \ in6p->inp_flags |= (bit); \ else \ in6p->inp_flags &= ~(bit); \ INP_WUNLOCK(in6p); \ } while (/*CONSTCOND*/ 0) #define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0) #define OPTSET2(bit, val) do { \ INP_WLOCK(in6p); \ if (val) \ in6p->inp_flags2 |= bit; \ else \ in6p->inp_flags2 &= ~bit; \ INP_WUNLOCK(in6p); \ } while (0) #define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0) case IPV6_RECVPKTINFO: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_PKTINFO); break; case IPV6_HOPLIMIT: { struct ip6_pktopts **optp; /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } optp = &in6p->in6p_outputopts; error = ip6_pcbopt(IPV6_HOPLIMIT, (u_char *)&optval, sizeof(optval), optp, (td != NULL) ? td->td_ucred : NULL, uproto); break; } case IPV6_RECVHOPLIMIT: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_HOPLIMIT); break; case IPV6_RECVHOPOPTS: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_HOPOPTS); break; case IPV6_RECVDSTOPTS: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_DSTOPTS); break; case IPV6_RECVRTHDRDSTOPTS: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_RTHDRDSTOPTS); break; case IPV6_RECVRTHDR: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_RTHDR); break; case IPV6_RECVPATHMTU: /* * We ignore this option for TCP * sockets. * (RFC3542 leaves this case * unspecified.) */ if (uproto != IPPROTO_TCP) OPTSET(IN6P_MTU); break; case IPV6_RECVFLOWID: OPTSET2(INP_RECVFLOWID, optval); break; #ifdef RSS case IPV6_RECVRSSBUCKETID: OPTSET2(INP_RECVRSSBUCKETID, optval); break; #endif case IPV6_V6ONLY: /* * make setsockopt(IPV6_V6ONLY) * available only prior to bind(2). * see ipng mailing list, Jun 22 2001. */ if (in6p->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { error = EINVAL; break; } OPTSET(IN6P_IPV6_V6ONLY); if (optval) in6p->inp_vflag &= ~INP_IPV4; else in6p->inp_vflag |= INP_IPV4; break; case IPV6_RECVTCLASS: /* cannot mix with RFC2292 XXX */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_TCLASS); break; case IPV6_AUTOFLOWLABEL: OPTSET(IN6P_AUTOFLOWLABEL); break; case IPV6_BINDANY: OPTSET(INP_BINDANY); break; case IPV6_BINDMULTI: OPTSET2(INP_BINDMULTI, optval); break; #ifdef RSS case IPV6_RSS_LISTEN_BUCKET: if ((optval >= 0) && (optval < rss_getnumbuckets())) { in6p->inp_rss_listen_bucket = optval; OPTSET2(INP_RSS_BUCKET_SET, 1); } else { error = EINVAL; } break; #endif } break; case IPV6_TCLASS: case IPV6_DONTFRAG: case IPV6_USE_MIN_MTU: case IPV6_PREFER_TEMPADDR: if (optlen != sizeof(optval)) { error = EINVAL; break; } error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; { struct ip6_pktopts **optp; optp = &in6p->in6p_outputopts; error = ip6_pcbopt(optname, (u_char *)&optval, sizeof(optval), optp, (td != NULL) ? td->td_ucred : NULL, uproto); break; } case IPV6_2292PKTINFO: case IPV6_2292HOPLIMIT: case IPV6_2292HOPOPTS: case IPV6_2292DSTOPTS: case IPV6_2292RTHDR: /* RFC 2292 */ if (optlen != sizeof(int)) { error = EINVAL; break; } error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (optname) { case IPV6_2292PKTINFO: OPTSET2292(IN6P_PKTINFO); break; case IPV6_2292HOPLIMIT: OPTSET2292(IN6P_HOPLIMIT); break; case IPV6_2292HOPOPTS: /* * Check super-user privilege. * See comments for IPV6_RECVHOPOPTS. */ if (td != NULL) { error = priv_check(td, PRIV_NETINET_SETHDROPTS); if (error) return (error); } OPTSET2292(IN6P_HOPOPTS); break; case IPV6_2292DSTOPTS: if (td != NULL) { error = priv_check(td, PRIV_NETINET_SETHDROPTS); if (error) return (error); } OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ break; case IPV6_2292RTHDR: OPTSET2292(IN6P_RTHDR); break; } break; case IPV6_PKTINFO: case IPV6_HOPOPTS: case IPV6_RTHDR: case IPV6_DSTOPTS: case IPV6_RTHDRDSTOPTS: case IPV6_NEXTHOP: { /* new advanced API (RFC3542) */ u_char *optbuf; u_char optbuf_storage[MCLBYTES]; int optlen; struct ip6_pktopts **optp; /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } /* * We only ensure valsize is not too large * here. Further validation will be done * later. */ error = sooptcopyin(sopt, optbuf_storage, sizeof(optbuf_storage), 0); if (error) break; optlen = sopt->sopt_valsize; optbuf = optbuf_storage; optp = &in6p->in6p_outputopts; error = ip6_pcbopt(optname, optbuf, optlen, optp, (td != NULL) ? td->td_ucred : NULL, uproto); break; } #undef OPTSET case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: case IPV6_MSFILTER: case MCAST_BLOCK_SOURCE: case MCAST_UNBLOCK_SOURCE: case MCAST_JOIN_GROUP: case MCAST_LEAVE_GROUP: case MCAST_JOIN_SOURCE_GROUP: case MCAST_LEAVE_SOURCE_GROUP: error = ip6_setmoptions(in6p, sopt); break; case IPV6_PORTRANGE: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; INP_WLOCK(in6p); switch (optval) { case IPV6_PORTRANGE_DEFAULT: in6p->inp_flags &= ~(INP_LOWPORT); in6p->inp_flags &= ~(INP_HIGHPORT); break; case IPV6_PORTRANGE_HIGH: in6p->inp_flags &= ~(INP_LOWPORT); in6p->inp_flags |= INP_HIGHPORT; break; case IPV6_PORTRANGE_LOW: in6p->inp_flags &= ~(INP_HIGHPORT); in6p->inp_flags |= INP_LOWPORT; break; default: error = EINVAL; break; } INP_WUNLOCK(in6p); break; #ifdef IPSEC case IPV6_IPSEC_POLICY: { caddr_t req; struct mbuf *m; if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; req = mtod(m, caddr_t); error = ipsec_set_policy(in6p, optname, req, m->m_len, (sopt->sopt_td != NULL) ? sopt->sopt_td->td_ucred : NULL); m_freem(m); break; } #endif /* IPSEC */ default: error = ENOPROTOOPT; break; } break; case SOPT_GET: switch (optname) { case IPV6_2292PKTOPTIONS: #ifdef IPV6_PKTOPTIONS case IPV6_PKTOPTIONS: #endif /* * RFC3542 (effectively) deprecated the * semantics of the 2292-style pktoptions. * Since it was not reliable in nature (i.e., * applications had to expect the lack of some * information after all), it would make sense * to simplify this part by always returning * empty data. */ sopt->sopt_valsize = 0; break; case IPV6_RECVHOPOPTS: case IPV6_RECVDSTOPTS: case IPV6_RECVRTHDRDSTOPTS: case IPV6_UNICAST_HOPS: case IPV6_RECVPKTINFO: case IPV6_RECVHOPLIMIT: case IPV6_RECVRTHDR: case IPV6_RECVPATHMTU: case IPV6_V6ONLY: case IPV6_PORTRANGE: case IPV6_RECVTCLASS: case IPV6_AUTOFLOWLABEL: case IPV6_BINDANY: case IPV6_FLOWID: case IPV6_FLOWTYPE: case IPV6_RECVFLOWID: #ifdef RSS case IPV6_RSSBUCKETID: case IPV6_RECVRSSBUCKETID: #endif + case IPV6_BINDMULTI: switch (optname) { case IPV6_RECVHOPOPTS: optval = OPTBIT(IN6P_HOPOPTS); break; case IPV6_RECVDSTOPTS: optval = OPTBIT(IN6P_DSTOPTS); break; case IPV6_RECVRTHDRDSTOPTS: optval = OPTBIT(IN6P_RTHDRDSTOPTS); break; case IPV6_UNICAST_HOPS: optval = in6p->in6p_hops; break; case IPV6_RECVPKTINFO: optval = OPTBIT(IN6P_PKTINFO); break; case IPV6_RECVHOPLIMIT: optval = OPTBIT(IN6P_HOPLIMIT); break; case IPV6_RECVRTHDR: optval = OPTBIT(IN6P_RTHDR); break; case IPV6_RECVPATHMTU: optval = OPTBIT(IN6P_MTU); break; case IPV6_V6ONLY: optval = OPTBIT(IN6P_IPV6_V6ONLY); break; case IPV6_PORTRANGE: { int flags; flags = in6p->inp_flags; if (flags & INP_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; else if (flags & INP_LOWPORT) optval = IPV6_PORTRANGE_LOW; else optval = 0; break; } case IPV6_RECVTCLASS: optval = OPTBIT(IN6P_TCLASS); break; case IPV6_AUTOFLOWLABEL: optval = OPTBIT(IN6P_AUTOFLOWLABEL); break; case IPV6_BINDANY: optval = OPTBIT(INP_BINDANY); break; case IPV6_FLOWID: optval = in6p->inp_flowid; break; case IPV6_FLOWTYPE: optval = in6p->inp_flowtype; break; case IPV6_RECVFLOWID: optval = OPTBIT2(INP_RECVFLOWID); break; #ifdef RSS case IPV6_RSSBUCKETID: retval = rss_hash2bucket(in6p->inp_flowid, in6p->inp_flowtype, &rss_bucket); if (retval == 0) optval = rss_bucket; else error = EINVAL; break; case IPV6_RECVRSSBUCKETID: optval = OPTBIT2(INP_RECVRSSBUCKETID); break; #endif case IPV6_BINDMULTI: optval = OPTBIT2(INP_BINDMULTI); break; } if (error) break; error = sooptcopyout(sopt, &optval, sizeof optval); break; case IPV6_PATHMTU: { u_long pmtu = 0; struct ip6_mtuinfo mtuinfo; struct route_in6 sro; bzero(&sro, sizeof(sro)); if (!(so->so_state & SS_ISCONNECTED)) return (ENOTCONN); /* * XXX: we dot not consider the case of source * routing, or optional information to specify * the outgoing interface. */ error = ip6_getpmtu(&sro, NULL, NULL, &in6p->in6p_faddr, &pmtu, NULL, so->so_fibnum); if (sro.ro_rt) RTFREE(sro.ro_rt); if (error) break; if (pmtu > IPV6_MAXPACKET) pmtu = IPV6_MAXPACKET; bzero(&mtuinfo, sizeof(mtuinfo)); mtuinfo.ip6m_mtu = (u_int32_t)pmtu; optdata = (void *)&mtuinfo; optdatalen = sizeof(mtuinfo); error = sooptcopyout(sopt, optdata, optdatalen); break; } case IPV6_2292PKTINFO: case IPV6_2292HOPLIMIT: case IPV6_2292HOPOPTS: case IPV6_2292RTHDR: case IPV6_2292DSTOPTS: switch (optname) { case IPV6_2292PKTINFO: optval = OPTBIT(IN6P_PKTINFO); break; case IPV6_2292HOPLIMIT: optval = OPTBIT(IN6P_HOPLIMIT); break; case IPV6_2292HOPOPTS: optval = OPTBIT(IN6P_HOPOPTS); break; case IPV6_2292RTHDR: optval = OPTBIT(IN6P_RTHDR); break; case IPV6_2292DSTOPTS: optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); break; } error = sooptcopyout(sopt, &optval, sizeof optval); break; case IPV6_PKTINFO: case IPV6_HOPOPTS: case IPV6_RTHDR: case IPV6_DSTOPTS: case IPV6_RTHDRDSTOPTS: case IPV6_NEXTHOP: case IPV6_TCLASS: case IPV6_DONTFRAG: case IPV6_USE_MIN_MTU: case IPV6_PREFER_TEMPADDR: error = ip6_getpcbopt(in6p->in6p_outputopts, optname, sopt); break; case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: case IPV6_MSFILTER: error = ip6_getmoptions(in6p, sopt); break; #ifdef IPSEC case IPV6_IPSEC_POLICY: { caddr_t req = NULL; size_t len = 0; struct mbuf *m = NULL; struct mbuf **mp = &m; size_t ovalsize = sopt->sopt_valsize; caddr_t oval = (caddr_t)sopt->sopt_val; error = soopt_getm(sopt, &m); /* XXX */ if (error != 0) break; error = soopt_mcopyin(sopt, m); /* XXX */ if (error != 0) break; sopt->sopt_valsize = ovalsize; sopt->sopt_val = oval; if (m) { req = mtod(m, caddr_t); len = m->m_len; } error = ipsec_get_policy(in6p, req, len, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /* XXX */ if (error == 0 && m) m_freem(m); break; } #endif /* IPSEC */ default: error = ENOPROTOOPT; break; } break; } } return (error); } int ip6_raw_ctloutput(struct socket *so, struct sockopt *sopt) { int error = 0, optval, optlen; const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); struct inpcb *in6p = sotoinpcb(so); int level, op, optname; level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; if (level != IPPROTO_IPV6) { return (EINVAL); } switch (optname) { case IPV6_CHECKSUM: /* * For ICMPv6 sockets, no modification allowed for checksum * offset, permit "no change" values to help existing apps. * * RFC3542 says: "An attempt to set IPV6_CHECKSUM * for an ICMPv6 socket will fail." * The current behavior does not meet RFC3542. */ switch (op) { case SOPT_SET: if (optlen != sizeof(int)) { error = EINVAL; break; } error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); if (error) break; if ((optval % 2) != 0) { /* the API assumes even offset values */ error = EINVAL; } else if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (optval != icmp6off) error = EINVAL; } else in6p->in6p_cksum = optval; break; case SOPT_GET: if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) optval = icmp6off; else optval = in6p->in6p_cksum; error = sooptcopyout(sopt, &optval, sizeof(optval)); break; default: error = EINVAL; break; } break; default: error = ENOPROTOOPT; break; } return (error); } /* * Set up IP6 options in pcb for insertion in output packets or * specifying behavior of outgoing packets. */ static int ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct socket *so, struct sockopt *sopt) { struct ip6_pktopts *opt = *pktopt; int error = 0; struct thread *td = sopt->sopt_td; /* turn off any old options. */ if (opt) { #ifdef DIAGNOSTIC if (opt->ip6po_pktinfo || opt->ip6po_nexthop || opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || opt->ip6po_rhinfo.ip6po_rhi_rthdr) printf("ip6_pcbopts: all specified options are cleared.\n"); #endif ip6_clearpktopts(opt, -1); } else opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); *pktopt = NULL; if (!m || m->m_len == 0) { /* * Only turning off any previous options, regardless of * whether the opt is just created or given. */ free(opt, M_IP6OPT); return (0); } /* set options specified by user. */ if ((error = ip6_setpktopts(m, opt, NULL, (td != NULL) ? td->td_ucred : NULL, so->so_proto->pr_protocol)) != 0) { ip6_clearpktopts(opt, -1); /* XXX: discard all options */ free(opt, M_IP6OPT); return (error); } *pktopt = opt; return (0); } /* * initialize ip6_pktopts. beware that there are non-zero default values in * the struct. */ void ip6_initpktopts(struct ip6_pktopts *opt) { bzero(opt, sizeof(*opt)); opt->ip6po_hlim = -1; /* -1 means default hop limit */ opt->ip6po_tclass = -1; /* -1 means default traffic class */ opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY; opt->ip6po_prefer_tempaddr = IP6PO_TEMPADDR_SYSTEM; } static int ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, struct ucred *cred, int uproto) { struct ip6_pktopts *opt; if (*pktopt == NULL) { *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT, M_WAITOK); ip6_initpktopts(*pktopt); } opt = *pktopt; return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto)); } static int ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt) { void *optdata = NULL; int optdatalen = 0; struct ip6_ext *ip6e; int error = 0; struct in6_pktinfo null_pktinfo; int deftclass = 0, on; int defminmtu = IP6PO_MINMTU_MCASTONLY; int defpreftemp = IP6PO_TEMPADDR_SYSTEM; switch (optname) { case IPV6_PKTINFO: optdata = (void *)&null_pktinfo; if (pktopt && pktopt->ip6po_pktinfo) { bcopy(pktopt->ip6po_pktinfo, &null_pktinfo, sizeof(null_pktinfo)); in6_clearscope(&null_pktinfo.ipi6_addr); } else { /* XXX: we don't have to do this every time... */ bzero(&null_pktinfo, sizeof(null_pktinfo)); } optdatalen = sizeof(struct in6_pktinfo); break; case IPV6_TCLASS: if (pktopt && pktopt->ip6po_tclass >= 0) optdata = (void *)&pktopt->ip6po_tclass; else optdata = (void *)&deftclass; optdatalen = sizeof(int); break; case IPV6_HOPOPTS: if (pktopt && pktopt->ip6po_hbh) { optdata = (void *)pktopt->ip6po_hbh; ip6e = (struct ip6_ext *)pktopt->ip6po_hbh; optdatalen = (ip6e->ip6e_len + 1) << 3; } break; case IPV6_RTHDR: if (pktopt && pktopt->ip6po_rthdr) { optdata = (void *)pktopt->ip6po_rthdr; ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr; optdatalen = (ip6e->ip6e_len + 1) << 3; } break; case IPV6_RTHDRDSTOPTS: if (pktopt && pktopt->ip6po_dest1) { optdata = (void *)pktopt->ip6po_dest1; ip6e = (struct ip6_ext *)pktopt->ip6po_dest1; optdatalen = (ip6e->ip6e_len + 1) << 3; } break; case IPV6_DSTOPTS: if (pktopt && pktopt->ip6po_dest2) { optdata = (void *)pktopt->ip6po_dest2; ip6e = (struct ip6_ext *)pktopt->ip6po_dest2; optdatalen = (ip6e->ip6e_len + 1) << 3; } break; case IPV6_NEXTHOP: if (pktopt && pktopt->ip6po_nexthop) { optdata = (void *)pktopt->ip6po_nexthop; optdatalen = pktopt->ip6po_nexthop->sa_len; } break; case IPV6_USE_MIN_MTU: if (pktopt) optdata = (void *)&pktopt->ip6po_minmtu; else optdata = (void *)&defminmtu; optdatalen = sizeof(int); break; case IPV6_DONTFRAG: if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG)) on = 1; else on = 0; optdata = (void *)&on; optdatalen = sizeof(on); break; case IPV6_PREFER_TEMPADDR: if (pktopt) optdata = (void *)&pktopt->ip6po_prefer_tempaddr; else optdata = (void *)&defpreftemp; optdatalen = sizeof(int); break; default: /* should not happen */ #ifdef DIAGNOSTIC panic("ip6_getpcbopt: unexpected option\n"); #endif return (ENOPROTOOPT); } error = sooptcopyout(sopt, optdata, optdatalen); return (error); } void ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname) { if (pktopt == NULL) return; if (optname == -1 || optname == IPV6_PKTINFO) { if (pktopt->ip6po_pktinfo) free(pktopt->ip6po_pktinfo, M_IP6OPT); pktopt->ip6po_pktinfo = NULL; } if (optname == -1 || optname == IPV6_HOPLIMIT) pktopt->ip6po_hlim = -1; if (optname == -1 || optname == IPV6_TCLASS) pktopt->ip6po_tclass = -1; if (optname == -1 || optname == IPV6_NEXTHOP) { if (pktopt->ip6po_nextroute.ro_rt) { RTFREE(pktopt->ip6po_nextroute.ro_rt); pktopt->ip6po_nextroute.ro_rt = NULL; } if (pktopt->ip6po_nexthop) free(pktopt->ip6po_nexthop, M_IP6OPT); pktopt->ip6po_nexthop = NULL; } if (optname == -1 || optname == IPV6_HOPOPTS) { if (pktopt->ip6po_hbh) free(pktopt->ip6po_hbh, M_IP6OPT); pktopt->ip6po_hbh = NULL; } if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) { if (pktopt->ip6po_dest1) free(pktopt->ip6po_dest1, M_IP6OPT); pktopt->ip6po_dest1 = NULL; } if (optname == -1 || optname == IPV6_RTHDR) { if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; if (pktopt->ip6po_route.ro_rt) { RTFREE(pktopt->ip6po_route.ro_rt); pktopt->ip6po_route.ro_rt = NULL; } } if (optname == -1 || optname == IPV6_DSTOPTS) { if (pktopt->ip6po_dest2) free(pktopt->ip6po_dest2, M_IP6OPT); pktopt->ip6po_dest2 = NULL; } } #define PKTOPT_EXTHDRCPY(type) \ do {\ if (src->type) {\ int hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ dst->type = malloc(hlen, M_IP6OPT, canwait);\ if (dst->type == NULL && canwait == M_NOWAIT)\ goto bad;\ bcopy(src->type, dst->type, hlen);\ }\ } while (/*CONSTCOND*/ 0) static int copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait) { if (dst == NULL || src == NULL) { printf("ip6_clearpktopts: invalid argument\n"); return (EINVAL); } dst->ip6po_hlim = src->ip6po_hlim; dst->ip6po_tclass = src->ip6po_tclass; dst->ip6po_flags = src->ip6po_flags; dst->ip6po_minmtu = src->ip6po_minmtu; dst->ip6po_prefer_tempaddr = src->ip6po_prefer_tempaddr; if (src->ip6po_pktinfo) { dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), M_IP6OPT, canwait); if (dst->ip6po_pktinfo == NULL) goto bad; *dst->ip6po_pktinfo = *src->ip6po_pktinfo; } if (src->ip6po_nexthop) { dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len, M_IP6OPT, canwait); if (dst->ip6po_nexthop == NULL) goto bad; bcopy(src->ip6po_nexthop, dst->ip6po_nexthop, src->ip6po_nexthop->sa_len); } PKTOPT_EXTHDRCPY(ip6po_hbh); PKTOPT_EXTHDRCPY(ip6po_dest1); PKTOPT_EXTHDRCPY(ip6po_dest2); PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */ return (0); bad: ip6_clearpktopts(dst, -1); return (ENOBUFS); } #undef PKTOPT_EXTHDRCPY struct ip6_pktopts * ip6_copypktopts(struct ip6_pktopts *src, int canwait) { int error; struct ip6_pktopts *dst; dst = malloc(sizeof(*dst), M_IP6OPT, canwait); if (dst == NULL) return (NULL); ip6_initpktopts(dst); if ((error = copypktopts(dst, src, canwait)) != 0) { free(dst, M_IP6OPT); return (NULL); } return (dst); } void ip6_freepcbopts(struct ip6_pktopts *pktopt) { if (pktopt == NULL) return; ip6_clearpktopts(pktopt, -1); free(pktopt, M_IP6OPT); } /* * Set IPv6 outgoing packet options based on advanced API. */ int ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt, struct ip6_pktopts *stickyopt, struct ucred *cred, int uproto) { struct cmsghdr *cm = 0; if (control == NULL || opt == NULL) return (EINVAL); ip6_initpktopts(opt); if (stickyopt) { int error; /* * If stickyopt is provided, make a local copy of the options * for this particular packet, then override them by ancillary * objects. * XXX: copypktopts() does not copy the cached route to a next * hop (if any). This is not very good in terms of efficiency, * but we can allow this since this option should be rarely * used. */ if ((error = copypktopts(opt, stickyopt, M_NOWAIT)) != 0) return (error); } /* * XXX: Currently, we assume all the optional information is stored * in a single mbuf. */ if (control->m_next) return (EINVAL); for (; control->m_len > 0; control->m_data += CMSG_ALIGN(cm->cmsg_len), control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { int error; if (control->m_len < CMSG_LEN(0)) return (EINVAL); cm = mtod(control, struct cmsghdr *); if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) return (EINVAL); if (cm->cmsg_level != IPPROTO_IPV6) continue; error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm), cm->cmsg_len - CMSG_LEN(0), opt, cred, 0, 1, uproto); if (error) return (error); } return (0); } /* * Set a particular packet option, as a sticky option or an ancillary data * item. "len" can be 0 only when it's a sticky option. * We have 4 cases of combination of "sticky" and "cmsg": * "sticky=0, cmsg=0": impossible * "sticky=0, cmsg=1": RFC2292 or RFC3542 ancillary data * "sticky=1, cmsg=0": RFC3542 socket option * "sticky=1, cmsg=1": RFC2292 socket option */ static int ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, struct ucred *cred, int sticky, int cmsg, int uproto) { int minmtupolicy, preftemp; int error; if (!sticky && !cmsg) { #ifdef DIAGNOSTIC printf("ip6_setpktopt: impossible case\n"); #endif return (EINVAL); } /* * IPV6_2292xxx is for backward compatibility to RFC2292, and should * not be specified in the context of RFC3542. Conversely, * RFC3542 types should not be specified in the context of RFC2292. */ if (!cmsg) { switch (optname) { case IPV6_2292PKTINFO: case IPV6_2292HOPLIMIT: case IPV6_2292NEXTHOP: case IPV6_2292HOPOPTS: case IPV6_2292DSTOPTS: case IPV6_2292RTHDR: case IPV6_2292PKTOPTIONS: return (ENOPROTOOPT); } } if (sticky && cmsg) { switch (optname) { case IPV6_PKTINFO: case IPV6_HOPLIMIT: case IPV6_NEXTHOP: case IPV6_HOPOPTS: case IPV6_DSTOPTS: case IPV6_RTHDRDSTOPTS: case IPV6_RTHDR: case IPV6_USE_MIN_MTU: case IPV6_DONTFRAG: case IPV6_TCLASS: case IPV6_PREFER_TEMPADDR: /* XXX: not an RFC3542 option */ return (ENOPROTOOPT); } } switch (optname) { case IPV6_2292PKTINFO: case IPV6_PKTINFO: { struct ifnet *ifp = NULL; struct in6_pktinfo *pktinfo; if (len != sizeof(struct in6_pktinfo)) return (EINVAL); pktinfo = (struct in6_pktinfo *)buf; /* * An application can clear any sticky IPV6_PKTINFO option by * doing a "regular" setsockopt with ipi6_addr being * in6addr_any and ipi6_ifindex being zero. * [RFC 3542, Section 6] */ if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo && pktinfo->ipi6_ifindex == 0 && IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { ip6_clearpktopts(opt, optname); break; } if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO && sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { return (EINVAL); } if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr)) return (EINVAL); /* validate the interface index if specified. */ if (pktinfo->ipi6_ifindex > V_if_index) return (ENXIO); if (pktinfo->ipi6_ifindex) { ifp = ifnet_byindex(pktinfo->ipi6_ifindex); if (ifp == NULL) return (ENXIO); } if (ifp != NULL && ( ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) return (ENETDOWN); if (ifp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { struct in6_ifaddr *ia; in6_setscope(&pktinfo->ipi6_addr, ifp, NULL); ia = in6ifa_ifpwithaddr(ifp, &pktinfo->ipi6_addr); if (ia == NULL) return (EADDRNOTAVAIL); ifa_free(&ia->ia_ifa); } /* * We store the address anyway, and let in6_selectsrc() * validate the specified address. This is because ipi6_addr * may not have enough information about its scope zone, and * we may need additional information (such as outgoing * interface or the scope zone of a destination address) to * disambiguate the scope. * XXX: the delay of the validation may confuse the * application when it is used as a sticky option. */ if (opt->ip6po_pktinfo == NULL) { opt->ip6po_pktinfo = malloc(sizeof(*pktinfo), M_IP6OPT, M_NOWAIT); if (opt->ip6po_pktinfo == NULL) return (ENOBUFS); } bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo)); break; } case IPV6_2292HOPLIMIT: case IPV6_HOPLIMIT: { int *hlimp; /* * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT * to simplify the ordering among hoplimit options. */ if (optname == IPV6_HOPLIMIT && sticky) return (ENOPROTOOPT); if (len != sizeof(int)) return (EINVAL); hlimp = (int *)buf; if (*hlimp < -1 || *hlimp > 255) return (EINVAL); opt->ip6po_hlim = *hlimp; break; } case IPV6_TCLASS: { int tclass; if (len != sizeof(int)) return (EINVAL); tclass = *(int *)buf; if (tclass < -1 || tclass > 255) return (EINVAL); opt->ip6po_tclass = tclass; break; } case IPV6_2292NEXTHOP: case IPV6_NEXTHOP: if (cred != NULL) { error = priv_check_cred(cred, PRIV_NETINET_SETHDROPTS, 0); if (error) return (error); } if (len == 0) { /* just remove the option */ ip6_clearpktopts(opt, IPV6_NEXTHOP); break; } /* check if cmsg_len is large enough for sa_len */ if (len < sizeof(struct sockaddr) || len < *buf) return (EINVAL); switch (((struct sockaddr *)buf)->sa_family) { case AF_INET6: { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf; int error; if (sa6->sin6_len != sizeof(struct sockaddr_in6)) return (EINVAL); if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) || IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { return (EINVAL); } if ((error = sa6_embedscope(sa6, V_ip6_use_defzone)) != 0) { return (error); } break; } case AF_LINK: /* should eventually be supported */ default: return (EAFNOSUPPORT); } /* turn off the previous option, then set the new option. */ ip6_clearpktopts(opt, IPV6_NEXTHOP); opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_NOWAIT); if (opt->ip6po_nexthop == NULL) return (ENOBUFS); bcopy(buf, opt->ip6po_nexthop, *buf); break; case IPV6_2292HOPOPTS: case IPV6_HOPOPTS: { struct ip6_hbh *hbh; int hbhlen; /* * XXX: We don't allow a non-privileged user to set ANY HbH * options, since per-option restriction has too much * overhead. */ if (cred != NULL) { error = priv_check_cred(cred, PRIV_NETINET_SETHDROPTS, 0); if (error) return (error); } if (len == 0) { ip6_clearpktopts(opt, IPV6_HOPOPTS); break; /* just remove the option */ } /* message length validation */ if (len < sizeof(struct ip6_hbh)) return (EINVAL); hbh = (struct ip6_hbh *)buf; hbhlen = (hbh->ip6h_len + 1) << 3; if (len != hbhlen) return (EINVAL); /* turn off the previous option, then set the new option. */ ip6_clearpktopts(opt, IPV6_HOPOPTS); opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT); if (opt->ip6po_hbh == NULL) return (ENOBUFS); bcopy(hbh, opt->ip6po_hbh, hbhlen); break; } case IPV6_2292DSTOPTS: case IPV6_DSTOPTS: case IPV6_RTHDRDSTOPTS: { struct ip6_dest *dest, **newdest = NULL; int destlen; if (cred != NULL) { /* XXX: see the comment for IPV6_HOPOPTS */ error = priv_check_cred(cred, PRIV_NETINET_SETHDROPTS, 0); if (error) return (error); } if (len == 0) { ip6_clearpktopts(opt, optname); break; /* just remove the option */ } /* message length validation */ if (len < sizeof(struct ip6_dest)) return (EINVAL); dest = (struct ip6_dest *)buf; destlen = (dest->ip6d_len + 1) << 3; if (len != destlen) return (EINVAL); /* * Determine the position that the destination options header * should be inserted; before or after the routing header. */ switch (optname) { case IPV6_2292DSTOPTS: /* * The old advacned API is ambiguous on this point. * Our approach is to determine the position based * according to the existence of a routing header. * Note, however, that this depends on the order of the * extension headers in the ancillary data; the 1st * part of the destination options header must appear * before the routing header in the ancillary data, * too. * RFC3542 solved the ambiguity by introducing * separate ancillary data or option types. */ if (opt->ip6po_rthdr == NULL) newdest = &opt->ip6po_dest1; else newdest = &opt->ip6po_dest2; break; case IPV6_RTHDRDSTOPTS: newdest = &opt->ip6po_dest1; break; case IPV6_DSTOPTS: newdest = &opt->ip6po_dest2; break; } /* turn off the previous option, then set the new option. */ ip6_clearpktopts(opt, optname); *newdest = malloc(destlen, M_IP6OPT, M_NOWAIT); if (*newdest == NULL) return (ENOBUFS); bcopy(dest, *newdest, destlen); break; } case IPV6_2292RTHDR: case IPV6_RTHDR: { struct ip6_rthdr *rth; int rthlen; if (len == 0) { ip6_clearpktopts(opt, IPV6_RTHDR); break; /* just remove the option */ } /* message length validation */ if (len < sizeof(struct ip6_rthdr)) return (EINVAL); rth = (struct ip6_rthdr *)buf; rthlen = (rth->ip6r_len + 1) << 3; if (len != rthlen) return (EINVAL); switch (rth->ip6r_type) { case IPV6_RTHDR_TYPE_0: if (rth->ip6r_len == 0) /* must contain one addr */ return (EINVAL); if (rth->ip6r_len % 2) /* length must be even */ return (EINVAL); if (rth->ip6r_len / 2 != rth->ip6r_segleft) return (EINVAL); break; default: return (EINVAL); /* not supported */ } /* turn off the previous option */ ip6_clearpktopts(opt, IPV6_RTHDR); opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT); if (opt->ip6po_rthdr == NULL) return (ENOBUFS); bcopy(rth, opt->ip6po_rthdr, rthlen); break; } case IPV6_USE_MIN_MTU: if (len != sizeof(int)) return (EINVAL); minmtupolicy = *(int *)buf; if (minmtupolicy != IP6PO_MINMTU_MCASTONLY && minmtupolicy != IP6PO_MINMTU_DISABLE && minmtupolicy != IP6PO_MINMTU_ALL) { return (EINVAL); } opt->ip6po_minmtu = minmtupolicy; break; case IPV6_DONTFRAG: if (len != sizeof(int)) return (EINVAL); if (uproto == IPPROTO_TCP || *(int *)buf == 0) { /* * we ignore this option for TCP sockets. * (RFC3542 leaves this case unspecified.) */ opt->ip6po_flags &= ~IP6PO_DONTFRAG; } else opt->ip6po_flags |= IP6PO_DONTFRAG; break; case IPV6_PREFER_TEMPADDR: if (len != sizeof(int)) return (EINVAL); preftemp = *(int *)buf; if (preftemp != IP6PO_TEMPADDR_SYSTEM && preftemp != IP6PO_TEMPADDR_NOTPREFER && preftemp != IP6PO_TEMPADDR_PREFER) { return (EINVAL); } opt->ip6po_prefer_tempaddr = preftemp; break; default: return (ENOPROTOOPT); } /* end of switch */ return (0); } /* * Routine called from ip6_output() to loop back a copy of an IP6 multicast * packet to the input queue of a specified interface. Note that this * calls the output routine of the loopback "driver", but with an interface * pointer that might NOT be &loif -- easier than replicating that code here. */ void ip6_mloopback(struct ifnet *ifp, const struct mbuf *m) { struct mbuf *copym; struct ip6_hdr *ip6; copym = m_copy(m, 0, M_COPYALL); if (copym == NULL) return; /* * Make sure to deep-copy IPv6 header portion in case the data * is in an mbuf cluster, so that we can safely override the IPv6 * header portion later. */ if (!M_WRITABLE(copym) || copym->m_len < sizeof(struct ip6_hdr)) { copym = m_pullup(copym, sizeof(struct ip6_hdr)); if (copym == NULL) return; } ip6 = mtod(copym, struct ip6_hdr *); /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { copym->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR; copym->m_pkthdr.csum_data = 0xffff; } if_simloop(ifp, copym, AF_INET6, 0); } /* * Chop IPv6 header off from the payload. */ static int ip6_splithdr(struct mbuf *m, struct ip6_exthdrs *exthdrs) { struct mbuf *mh; struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); if (m->m_len > sizeof(*ip6)) { mh = m_gethdr(M_NOWAIT, MT_DATA); if (mh == NULL) { m_freem(m); return ENOBUFS; } m_move_pkthdr(mh, m); M_ALIGN(mh, sizeof(*ip6)); m->m_len -= sizeof(*ip6); m->m_data += sizeof(*ip6); mh->m_next = m; m = mh; m->m_len = sizeof(*ip6); bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6)); } exthdrs->ip6e_ip6 = m; return 0; } /* * Compute IPv6 extension header length. */ int ip6_optlen(struct inpcb *in6p) { int len; if (!in6p->in6p_outputopts) return 0; len = 0; #define elen(x) \ (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) len += elen(in6p->in6p_outputopts->ip6po_hbh); if (in6p->in6p_outputopts->ip6po_rthdr) /* dest1 is valid with rthdr only */ len += elen(in6p->in6p_outputopts->ip6po_dest1); len += elen(in6p->in6p_outputopts->ip6po_rthdr); len += elen(in6p->in6p_outputopts->ip6po_dest2); return len; #undef elen } Index: projects/clang380-import/sys/netinet6/nd6.c =================================================================== --- projects/clang380-import/sys/netinet6/nd6.c (revision 293015) +++ projects/clang380-import/sys/netinet6/nd6.c (revision 293016) @@ -1,2569 +1,2629 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: nd6.c,v 1.144 2001/05/24 07:44:00 itojun Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ #define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ #define SIN6(s) ((const struct sockaddr_in6 *)(s)) /* timer values */ VNET_DEFINE(int, nd6_prune) = 1; /* walk list every 1 seconds */ VNET_DEFINE(int, nd6_delay) = 5; /* delay first probe time 5 second */ VNET_DEFINE(int, nd6_umaxtries) = 3; /* maximum unicast query */ VNET_DEFINE(int, nd6_mmaxtries) = 3; /* maximum multicast query */ VNET_DEFINE(int, nd6_useloopback) = 1; /* use loopback interface for * local traffic */ VNET_DEFINE(int, nd6_gctimer) = (60 * 60 * 24); /* 1 day: garbage * collection timer */ /* preventing too many loops in ND option parsing */ static VNET_DEFINE(int, nd6_maxndopt) = 10; /* max # of ND options allowed */ VNET_DEFINE(int, nd6_maxnudhint) = 0; /* max # of subsequent upper * layer hints */ static VNET_DEFINE(int, nd6_maxqueuelen) = 1; /* max pkts cached in unresolved * ND entries */ #define V_nd6_maxndopt VNET(nd6_maxndopt) #define V_nd6_maxqueuelen VNET(nd6_maxqueuelen) #ifdef ND6_DEBUG VNET_DEFINE(int, nd6_debug) = 1; #else VNET_DEFINE(int, nd6_debug) = 0; #endif -static eventhandler_tag lle_event_eh; +static eventhandler_tag lle_event_eh, iflladdr_event_eh; /* for debugging? */ #if 0 static int nd6_inuse, nd6_allocated; #endif VNET_DEFINE(struct nd_drhead, nd_defrouter); VNET_DEFINE(struct nd_prhead, nd_prefix); VNET_DEFINE(int, nd6_recalc_reachtm_interval) = ND6_RECALC_REACHTM_INTERVAL; #define V_nd6_recalc_reachtm_interval VNET(nd6_recalc_reachtm_interval) int (*send_sendso_input_hook)(struct mbuf *, struct ifnet *, int, int); static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); static void nd6_slowtimo(void *); static int regen_tmpaddr(struct in6_ifaddr *); static void nd6_free(struct llentry *, int); static void nd6_free_redirect(const struct llentry *); static void nd6_llinfo_timer(void *); static void nd6_llinfo_settimer_locked(struct llentry *, long); static void clear_llinfo_pqueue(struct llentry *); static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); -static int nd6_resolve_slow(struct ifnet *, struct mbuf *, +static int nd6_resolve_slow(struct ifnet *, int, struct mbuf *, const struct sockaddr_in6 *, u_char *, uint32_t *); static int nd6_need_cache(struct ifnet *); static VNET_DEFINE(struct callout, nd6_slowtimo_ch); #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) VNET_DEFINE(struct callout, nd6_timer_ch); static void nd6_lle_event(void *arg __unused, struct llentry *lle, int evt) { struct rt_addrinfo rtinfo; struct sockaddr_in6 dst; struct sockaddr_dl gw; struct ifnet *ifp; int type; LLE_WLOCK_ASSERT(lle); if (lltable_get_af(lle->lle_tbl) != AF_INET6) return; switch (evt) { case LLENTRY_RESOLVED: type = RTM_ADD; KASSERT(lle->la_flags & LLE_VALID, ("%s: %p resolved but not valid?", __func__, lle)); break; case LLENTRY_EXPIRED: type = RTM_DELETE; break; default: return; } ifp = lltable_get_ifp(lle->lle_tbl); bzero(&dst, sizeof(dst)); bzero(&gw, sizeof(gw)); bzero(&rtinfo, sizeof(rtinfo)); lltable_fill_sa_entry(lle, (struct sockaddr *)&dst); dst.sin6_scope_id = in6_getscopezone(ifp, in6_addrscope(&dst.sin6_addr)); gw.sdl_len = sizeof(struct sockaddr_dl); gw.sdl_family = AF_LINK; gw.sdl_alen = ifp->if_addrlen; gw.sdl_index = ifp->if_index; gw.sdl_type = ifp->if_type; if (evt == LLENTRY_RESOLVED) - bcopy(&lle->ll_addr, gw.sdl_data, ifp->if_addrlen); + bcopy(lle->ll_addr, gw.sdl_data, ifp->if_addrlen); rtinfo.rti_info[RTAX_DST] = (struct sockaddr *)&dst; rtinfo.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&gw; rtinfo.rti_addrs = RTA_DST | RTA_GATEWAY; rt_missmsg_fib(type, &rtinfo, RTF_HOST | RTF_LLDATA | ( type == RTM_ADD ? RTF_UP: 0), 0, RT_DEFAULT_FIB); } +/* + * A handler for interface link layer address change event. + */ +static void +nd6_iflladdr(void *arg __unused, struct ifnet *ifp) +{ + + lltable_update_ifaddr(LLTABLE6(ifp)); +} + void nd6_init(void) { LIST_INIT(&V_nd_prefix); /* initialization of the default router list */ TAILQ_INIT(&V_nd_defrouter); /* start timer */ callout_init(&V_nd6_slowtimo_ch, 0); callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, curvnet); nd6_dad_init(); - if (IS_DEFAULT_VNET(curvnet)) + if (IS_DEFAULT_VNET(curvnet)) { lle_event_eh = EVENTHANDLER_REGISTER(lle_event, nd6_lle_event, NULL, EVENTHANDLER_PRI_ANY); + iflladdr_event_eh = EVENTHANDLER_REGISTER(iflladdr_event, + nd6_iflladdr, NULL, EVENTHANDLER_PRI_ANY); + } } #ifdef VIMAGE void nd6_destroy() { callout_drain(&V_nd6_slowtimo_ch); callout_drain(&V_nd6_timer_ch); - if (IS_DEFAULT_VNET(curvnet)) + if (IS_DEFAULT_VNET(curvnet)) { EVENTHANDLER_DEREGISTER(lle_event, lle_event_eh); + EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_event_eh); + } } #endif struct nd_ifinfo * nd6_ifattach(struct ifnet *ifp) { struct nd_ifinfo *nd; nd = (struct nd_ifinfo *)malloc(sizeof(*nd), M_IP6NDP, M_WAITOK|M_ZERO); nd->initialized = 1; nd->chlim = IPV6_DEFHLIM; nd->basereachable = REACHABLE_TIME; nd->reachable = ND_COMPUTE_RTIME(nd->basereachable); nd->retrans = RETRANS_TIMER; nd->flags = ND6_IFF_PERFORMNUD; /* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL. * XXXHRS: Clear ND6_IFF_AUTO_LINKLOCAL on an IFT_BRIDGE interface by * default regardless of the V_ip6_auto_linklocal configuration to * give a reasonable default behavior. */ if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) || (ifp->if_flags & IFF_LOOPBACK)) nd->flags |= ND6_IFF_AUTO_LINKLOCAL; /* * A loopback interface does not need to accept RTADV. * XXXHRS: Clear ND6_IFF_ACCEPT_RTADV on an IFT_BRIDGE interface by * default regardless of the V_ip6_accept_rtadv configuration to * prevent the interface from accepting RA messages arrived * on one of the member interfaces with ND6_IFF_ACCEPT_RTADV. */ if (V_ip6_accept_rtadv && !(ifp->if_flags & IFF_LOOPBACK) && (ifp->if_type != IFT_BRIDGE)) nd->flags |= ND6_IFF_ACCEPT_RTADV; if (V_ip6_no_radr && !(ifp->if_flags & IFF_LOOPBACK)) nd->flags |= ND6_IFF_NO_RADR; /* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */ nd6_setmtu0(ifp, nd); return nd; } void nd6_ifdetach(struct nd_ifinfo *nd) { free(nd, M_IP6NDP); } /* * Reset ND level link MTU. This function is called when the physical MTU * changes, which means we might have to adjust the ND level MTU. */ void nd6_setmtu(struct ifnet *ifp) { if (ifp->if_afdata[AF_INET6] == NULL) return; nd6_setmtu0(ifp, ND_IFINFO(ifp)); } /* XXX todo: do not maintain copy of ifp->if_mtu in ndi->maxmtu */ void nd6_setmtu0(struct ifnet *ifp, struct nd_ifinfo *ndi) { u_int32_t omaxmtu; omaxmtu = ndi->maxmtu; switch (ifp->if_type) { case IFT_ARCNET: ndi->maxmtu = MIN(ARC_PHDS_MAXMTU, ifp->if_mtu); /* RFC2497 */ break; case IFT_FDDI: ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); /* RFC2467 */ break; case IFT_ISO88025: ndi->maxmtu = MIN(ISO88025_MAX_MTU, ifp->if_mtu); break; default: ndi->maxmtu = ifp->if_mtu; break; } /* * Decreasing the interface MTU under IPV6 minimum MTU may cause * undesirable situation. We thus notify the operator of the change * explicitly. The check for omaxmtu is necessary to restrict the * log to the case of changing the MTU, not initializing it. */ if (omaxmtu >= IPV6_MMTU && ndi->maxmtu < IPV6_MMTU) { log(LOG_NOTICE, "nd6_setmtu0: " "new link MTU on %s (%lu) is too small for IPv6\n", if_name(ifp), (unsigned long)ndi->maxmtu); } if (ndi->maxmtu > V_in6_maxmtu) in6_setmaxmtu(); /* check all interfaces just in case */ } void nd6_option_init(void *opt, int icmp6len, union nd_opts *ndopts) { bzero(ndopts, sizeof(*ndopts)); ndopts->nd_opts_search = (struct nd_opt_hdr *)opt; ndopts->nd_opts_last = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len); if (icmp6len == 0) { ndopts->nd_opts_done = 1; ndopts->nd_opts_search = NULL; } } /* * Take one ND option. */ struct nd_opt_hdr * nd6_option(union nd_opts *ndopts) { struct nd_opt_hdr *nd_opt; int olen; KASSERT(ndopts != NULL, ("%s: ndopts == NULL", __func__)); KASSERT(ndopts->nd_opts_last != NULL, ("%s: uninitialized ndopts", __func__)); if (ndopts->nd_opts_search == NULL) return NULL; if (ndopts->nd_opts_done) return NULL; nd_opt = ndopts->nd_opts_search; /* make sure nd_opt_len is inside the buffer */ if ((caddr_t)&nd_opt->nd_opt_len >= (caddr_t)ndopts->nd_opts_last) { bzero(ndopts, sizeof(*ndopts)); return NULL; } olen = nd_opt->nd_opt_len << 3; if (olen == 0) { /* * Message validation requires that all included * options have a length that is greater than zero. */ bzero(ndopts, sizeof(*ndopts)); return NULL; } ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen); if (ndopts->nd_opts_search > ndopts->nd_opts_last) { /* option overruns the end of buffer, invalid */ bzero(ndopts, sizeof(*ndopts)); return NULL; } else if (ndopts->nd_opts_search == ndopts->nd_opts_last) { /* reached the end of options chain */ ndopts->nd_opts_done = 1; ndopts->nd_opts_search = NULL; } return nd_opt; } /* * Parse multiple ND options. * This function is much easier to use, for ND routines that do not need * multiple options of the same type. */ int nd6_options(union nd_opts *ndopts) { struct nd_opt_hdr *nd_opt; int i = 0; KASSERT(ndopts != NULL, ("%s: ndopts == NULL", __func__)); KASSERT(ndopts->nd_opts_last != NULL, ("%s: uninitialized ndopts", __func__)); if (ndopts->nd_opts_search == NULL) return 0; while (1) { nd_opt = nd6_option(ndopts); if (nd_opt == NULL && ndopts->nd_opts_last == NULL) { /* * Message validation requires that all included * options have a length that is greater than zero. */ ICMP6STAT_INC(icp6s_nd_badopt); bzero(ndopts, sizeof(*ndopts)); return -1; } if (nd_opt == NULL) goto skip1; switch (nd_opt->nd_opt_type) { case ND_OPT_SOURCE_LINKADDR: case ND_OPT_TARGET_LINKADDR: case ND_OPT_MTU: case ND_OPT_REDIRECTED_HEADER: case ND_OPT_NONCE: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { nd6log((LOG_INFO, "duplicated ND6 option found (type=%d)\n", nd_opt->nd_opt_type)); /* XXX bark? */ } else { ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; } break; case ND_OPT_PREFIX_INFORMATION: if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) { ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; } ndopts->nd_opts_pi_end = (struct nd_opt_prefix_info *)nd_opt; break; /* What about ND_OPT_ROUTE_INFO? RFC 4191 */ case ND_OPT_RDNSS: /* RFC 6106 */ case ND_OPT_DNSSL: /* RFC 6106 */ /* * Silently ignore options we know and do not care about * in the kernel. */ break; default: /* * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ nd6log((LOG_DEBUG, "nd6_options: unsupported option %d - " "option ignored\n", nd_opt->nd_opt_type)); } skip1: i++; if (i > V_nd6_maxndopt) { ICMP6STAT_INC(icp6s_nd_toomanyopt); nd6log((LOG_INFO, "too many loop in nd opt\n")); break; } if (ndopts->nd_opts_done) break; } return 0; } /* * ND6 timer routine to handle ND6 entries */ static void nd6_llinfo_settimer_locked(struct llentry *ln, long tick) { int canceled; LLE_WLOCK_ASSERT(ln); if (tick < 0) { ln->la_expire = 0; ln->ln_ntick = 0; canceled = callout_stop(&ln->lle_timer); } else { ln->la_expire = time_uptime + tick / hz; LLE_ADDREF(ln); if (tick > INT_MAX) { ln->ln_ntick = tick - INT_MAX; canceled = callout_reset(&ln->lle_timer, INT_MAX, nd6_llinfo_timer, ln); } else { ln->ln_ntick = 0; canceled = callout_reset(&ln->lle_timer, tick, nd6_llinfo_timer, ln); } } if (canceled > 0) LLE_REMREF(ln); } /* * Gets source address of the first packet in hold queue * and stores it in @src. * Returns pointer to @src (if hold queue is not empty) or NULL. * * Set noinline to be dtrace-friendly */ static __noinline struct in6_addr * nd6_llinfo_get_holdsrc(struct llentry *ln, struct in6_addr *src) { struct ip6_hdr hdr; struct mbuf *m; if (ln->la_hold == NULL) return (NULL); /* * assume every packet in la_hold has the same IP header */ m = ln->la_hold; if (sizeof(hdr) > m->m_len) return (NULL); m_copydata(m, 0, sizeof(hdr), (caddr_t)&hdr); *src = hdr.ip6_src; return (src); } /* * Checks if we need to switch from STALE state. * * RFC 4861 requires switching from STALE to DELAY state * on first packet matching entry, waiting V_nd6_delay and * transition to PROBE state (if upper layer confirmation was * not received). * * This code performs a bit differently: * On packet hit we don't change state (but desired state * can be guessed by control plane). However, after V_nd6_delay * seconds code will transition to PROBE state (so DELAY state * is kinda skipped in most situations). * * Typically, V_nd6_gctimer is bigger than V_nd6_delay, so * we perform the following upon entering STALE state: * * 1) Arm timer to run each V_nd6_delay seconds to make sure that * if packet was transmitted at the start of given interval, we * would be able to switch to PROBE state in V_nd6_delay seconds * as user expects. * * 2) Reschedule timer until original V_nd6_gctimer expires keeping * lle in STALE state (remaining timer value stored in lle_remtime). * * 3) Reschedule timer if packet was transmitted less that V_nd6_delay * seconds ago. * * Returns non-zero value if the entry is still STALE (storing * the next timer interval in @pdelay). * * Returns zero value if original timer expired or we need to switch to * PROBE (store that in @do_switch variable). */ static int nd6_is_stale(struct llentry *lle, long *pdelay, int *do_switch) { int nd_delay, nd_gctimer, r_skip_req; time_t lle_hittime; long delay; *do_switch = 0; nd_gctimer = V_nd6_gctimer; nd_delay = V_nd6_delay; LLE_REQ_LOCK(lle); r_skip_req = lle->r_skip_req; lle_hittime = lle->lle_hittime; LLE_REQ_UNLOCK(lle); if (r_skip_req > 0) { /* * Nonzero r_skip_req value was set upon entering * STALE state. Since value was not changed, no * packets were passed using this lle. Ask for * timer reschedule and keep STALE state. */ delay = (long)(MIN(nd_gctimer, nd_delay)); delay *= hz; if (lle->lle_remtime > delay) lle->lle_remtime -= delay; else { delay = lle->lle_remtime; lle->lle_remtime = 0; } if (delay == 0) { /* * The original ng6_gctime timeout ended, * no more rescheduling. */ return (0); } *pdelay = delay; return (1); } /* * Packet received. Verify timestamp */ delay = (long)(time_uptime - lle_hittime); if (delay < nd_delay) { /* * V_nd6_delay still not passed since the first * hit in STALE state. * Reshedule timer and return. */ *pdelay = (long)(nd_delay - delay) * hz; return (1); } /* Request switching to probe */ *do_switch = 1; return (0); } /* * Switch @lle state to new state optionally arming timers. * * Set noinline to be dtrace-friendly */ __noinline void nd6_llinfo_setstate(struct llentry *lle, int newstate) { struct ifnet *ifp; int nd_gctimer, nd_delay; long delay, remtime; delay = 0; remtime = 0; switch (newstate) { case ND6_LLINFO_INCOMPLETE: ifp = lle->lle_tbl->llt_ifp; delay = (long)ND_IFINFO(ifp)->retrans * hz / 1000; break; case ND6_LLINFO_REACHABLE: if (!ND6_LLINFO_PERMANENT(lle)) { ifp = lle->lle_tbl->llt_ifp; delay = (long)ND_IFINFO(ifp)->reachable * hz; } break; case ND6_LLINFO_STALE: /* * Notify fast path that we want to know if any packet * is transmitted by setting r_skip_req. */ LLE_REQ_LOCK(lle); lle->r_skip_req = 1; LLE_REQ_UNLOCK(lle); nd_delay = V_nd6_delay; nd_gctimer = V_nd6_gctimer; delay = (long)(MIN(nd_gctimer, nd_delay)) * hz; remtime = (long)nd_gctimer * hz - delay; break; case ND6_LLINFO_DELAY: lle->la_asked = 0; delay = (long)V_nd6_delay * hz; break; } if (delay > 0) nd6_llinfo_settimer_locked(lle, delay); lle->lle_remtime = remtime; lle->ln_state = newstate; } /* * Timer-dependent part of nd state machine. * * Set noinline to be dtrace-friendly */ static __noinline void nd6_llinfo_timer(void *arg) { struct llentry *ln; struct in6_addr *dst, *pdst, *psrc, src; struct ifnet *ifp; struct nd_ifinfo *ndi = NULL; int do_switch, send_ns; long delay; KASSERT(arg != NULL, ("%s: arg NULL", __func__)); ln = (struct llentry *)arg; LLE_WLOCK(ln); if (callout_pending(&ln->lle_timer)) { /* * Here we are a bit odd here in the treatment of * active/pending. If the pending bit is set, it got * rescheduled before I ran. The active * bit we ignore, since if it was stopped * in ll_tablefree() and was currently running * it would have return 0 so the code would * not have deleted it since the callout could * not be stopped so we want to go through * with the delete here now. If the callout * was restarted, the pending bit will be back on and * we just want to bail since the callout_reset would * return 1 and our reference would have been removed * by nd6_llinfo_settimer_locked above since canceled * would have been 1. */ LLE_WUNLOCK(ln); return; } ifp = ln->lle_tbl->llt_ifp; CURVNET_SET(ifp->if_vnet); ndi = ND_IFINFO(ifp); send_ns = 0; dst = &ln->r_l3addr.addr6; pdst = dst; if (ln->ln_ntick > 0) { if (ln->ln_ntick > INT_MAX) { ln->ln_ntick -= INT_MAX; nd6_llinfo_settimer_locked(ln, INT_MAX); } else { ln->ln_ntick = 0; nd6_llinfo_settimer_locked(ln, ln->ln_ntick); } goto done; } if (ln->la_flags & LLE_STATIC) { goto done; } if (ln->la_flags & LLE_DELETED) { nd6_free(ln, 0); ln = NULL; goto done; } switch (ln->ln_state) { case ND6_LLINFO_INCOMPLETE: if (ln->la_asked < V_nd6_mmaxtries) { ln->la_asked++; send_ns = 1; /* Send NS to multicast address */ pdst = NULL; } else { struct mbuf *m = ln->la_hold; if (m) { struct mbuf *m0; /* * assuming every packet in la_hold has the * same IP header. Send error after unlock. */ m0 = m->m_nextpkt; m->m_nextpkt = NULL; ln->la_hold = m0; clear_llinfo_pqueue(ln); } EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_TIMEDOUT); nd6_free(ln, 0); ln = NULL; if (m != NULL) icmp6_error2(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0, ifp); } break; case ND6_LLINFO_REACHABLE: if (!ND6_LLINFO_PERMANENT(ln)) nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); break; case ND6_LLINFO_STALE: if (nd6_is_stale(ln, &delay, &do_switch) != 0) { /* * No packet has used this entry and GC timeout * has not been passed. Reshedule timer and * return. */ nd6_llinfo_settimer_locked(ln, delay); break; } if (do_switch == 0) { /* * GC timer has ended and entry hasn't been used. * Run Garbage collector (RFC 4861, 5.3) */ if (!ND6_LLINFO_PERMANENT(ln)) { EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); nd6_free(ln, 1); ln = NULL; } break; } /* Entry has been used AND delay timer has ended. */ /* FALLTHROUGH */ case ND6_LLINFO_DELAY: if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { /* We need NUD */ ln->la_asked = 1; nd6_llinfo_setstate(ln, ND6_LLINFO_PROBE); send_ns = 1; } else nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); /* XXX */ break; case ND6_LLINFO_PROBE: if (ln->la_asked < V_nd6_umaxtries) { ln->la_asked++; send_ns = 1; } else { EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED); nd6_free(ln, 0); ln = NULL; } break; default: panic("%s: paths in a dark night can be confusing: %d", __func__, ln->ln_state); } done: if (send_ns != 0) { nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000); psrc = nd6_llinfo_get_holdsrc(ln, &src); LLE_FREE_LOCKED(ln); ln = NULL; nd6_ns_output(ifp, psrc, pdst, dst, NULL); } if (ln != NULL) LLE_FREE_LOCKED(ln); CURVNET_RESTORE(); } /* * ND6 timer routine to expire default route list and prefix list */ void nd6_timer(void *arg) { CURVNET_SET((struct vnet *) arg); struct nd_defrouter *dr, *ndr; struct nd_prefix *pr, *npr; struct in6_ifaddr *ia6, *nia6; callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz, nd6_timer, curvnet); /* expire default router list */ TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (dr->expire && dr->expire < time_uptime) defrtrlist_del(dr); } /* * expire interface addresses. * in the past the loop was inside prefix expiry processing. * However, from a stricter speci-confrmance standpoint, we should * rather separate address lifetimes and prefix lifetimes. * * XXXRW: in6_ifaddrhead locking. */ addrloop: TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) { /* check address lifetime */ if (IFA6_IS_INVALID(ia6)) { int regen = 0; /* * If the expiring address is temporary, try * regenerating a new one. This would be useful when * we suspended a laptop PC, then turned it on after a * period that could invalidate all temporary * addresses. Although we may have to restart the * loop (see below), it must be after purging the * address. Otherwise, we'd see an infinite loop of * regeneration. */ if (V_ip6_use_tempaddr && (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { if (regen_tmpaddr(ia6) == 0) regen = 1; } in6_purgeaddr(&ia6->ia_ifa); if (regen) goto addrloop; /* XXX: see below */ } else if (IFA6_IS_DEPRECATED(ia6)) { int oldflags = ia6->ia6_flags; ia6->ia6_flags |= IN6_IFF_DEPRECATED; /* * If a temporary address has just become deprecated, * regenerate a new one if possible. */ if (V_ip6_use_tempaddr && (ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && (oldflags & IN6_IFF_DEPRECATED) == 0) { if (regen_tmpaddr(ia6) == 0) { /* * A new temporary address is * generated. * XXX: this means the address chain * has changed while we are still in * the loop. Although the change * would not cause disaster (because * it's not a deletion, but an * addition,) we'd rather restart the * loop just for safety. Or does this * significantly reduce performance?? */ goto addrloop; } } } else if ((ia6->ia6_flags & IN6_IFF_TENTATIVE) != 0) { /* * Schedule DAD for a tentative address. This happens * if the interface was down or not running * when the address was configured. */ int delay; delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); nd6_dad_start((struct ifaddr *)ia6, delay); } else { /* * Check status of the interface. If it is down, * mark the address as tentative for future DAD. */ if ((ia6->ia_ifp->if_flags & IFF_UP) == 0 || (ia6->ia_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ND_IFINFO(ia6->ia_ifp)->flags & ND6_IFF_IFDISABLED) != 0) { ia6->ia6_flags &= ~IN6_IFF_DUPLICATED; ia6->ia6_flags |= IN6_IFF_TENTATIVE; } /* * A new RA might have made a deprecated address * preferred. */ ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; } } /* expire prefix list */ LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) { /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for * prefix is not necessary. */ if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && time_uptime - pr->ndpr_lastupdate > pr->ndpr_vltime) { /* * address expiration and prefix expiration are * separate. NEVER perform in6_purgeaddr here. */ prelist_remove(pr); } } CURVNET_RESTORE(); } /* * ia6 - deprecated/invalidated temporary address */ static int regen_tmpaddr(struct in6_ifaddr *ia6) { struct ifaddr *ifa; struct ifnet *ifp; struct in6_ifaddr *public_ifa6 = NULL; ifp = ia6->ia_ifa.ifa_ifp; IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in6_ifaddr *it6; if (ifa->ifa_addr->sa_family != AF_INET6) continue; it6 = (struct in6_ifaddr *)ifa; /* ignore no autoconf addresses. */ if ((it6->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; /* ignore autoconf addresses with different prefixes. */ if (it6->ia6_ndpr == NULL || it6->ia6_ndpr != ia6->ia6_ndpr) continue; /* * Now we are looking at an autoconf address with the same * prefix as ours. If the address is temporary and is still * preferred, do not create another one. It would be rare, but * could happen, for example, when we resume a laptop PC after * a long period. */ if ((it6->ia6_flags & IN6_IFF_TEMPORARY) != 0 && !IFA6_IS_DEPRECATED(it6)) { public_ifa6 = NULL; break; } /* * This is a public autoconf address that has the same prefix * as ours. If it is preferred, keep it. We can't break the * loop here, because there may be a still-preferred temporary * address with the prefix. */ if (!IFA6_IS_DEPRECATED(it6)) public_ifa6 = it6; } if (public_ifa6 != NULL) ifa_ref(&public_ifa6->ia_ifa); IF_ADDR_RUNLOCK(ifp); if (public_ifa6 != NULL) { int e; if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) { ifa_free(&public_ifa6->ia_ifa); log(LOG_NOTICE, "regen_tmpaddr: failed to create a new" " tmp addr,errno=%d\n", e); return (-1); } ifa_free(&public_ifa6->ia_ifa); return (0); } return (-1); } /* * Nuke neighbor cache/prefix/default router management table, right before * ifp goes away. */ void nd6_purge(struct ifnet *ifp) { struct nd_defrouter *dr, *ndr; struct nd_prefix *pr, *npr; /* * Nuke default router list entries toward ifp. * We defer removal of default router list entries that is installed * in the routing table, in order to keep additional side effects as * small as possible. */ TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (dr->installed) continue; if (dr->ifp == ifp) defrtrlist_del(dr); } TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (!dr->installed) continue; if (dr->ifp == ifp) defrtrlist_del(dr); } /* Nuke prefix list entries toward ifp */ LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) { if (pr->ndpr_ifp == ifp) { /* * Because if_detach() does *not* release prefixes * while purging addresses the reference count will * still be above zero. We therefore reset it to * make sure that the prefix really gets purged. */ pr->ndpr_refcnt = 0; /* * Previously, pr->ndpr_addr is removed as well, * but I strongly believe we don't have to do it. * nd6_purge() is only called from in6_ifdetach(), * which removes all the associated interface addresses * by itself. * (jinmei@kame.net 20010129) */ prelist_remove(pr); } } /* cancel default outgoing interface setting */ if (V_nd6_defifindex == ifp->if_index) nd6_setdefaultiface(0); if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { /* Refresh default router list. */ defrouter_select(); } /* XXXXX * We do not nuke the neighbor cache entries here any more * because the neighbor cache is kept in if_afdata[AF_INET6]. * nd6_purge() is invoked by in6_ifdetach() which is called * from if_detach() where everything gets purged. So let * in6_domifdetach() do the actual L2 table purging work. */ } /* * the caller acquires and releases the lock on the lltbls * Returns the llentry locked */ struct llentry * nd6_lookup(const struct in6_addr *addr6, int flags, struct ifnet *ifp) { struct sockaddr_in6 sin6; struct llentry *ln; bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; IF_AFDATA_LOCK_ASSERT(ifp); ln = lla_lookup(LLTABLE6(ifp), flags, (struct sockaddr *)&sin6); return (ln); } struct llentry * nd6_alloc(const struct in6_addr *addr6, int flags, struct ifnet *ifp) { struct sockaddr_in6 sin6; struct llentry *ln; bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; ln = lltable_alloc_entry(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6); if (ln != NULL) ln->ln_state = ND6_LLINFO_NOSTATE; return (ln); } /* * Test whether a given IPv6 address is a neighbor or not, ignoring * the actual neighbor cache. The neighbor cache is ignored in order * to not reenter the routing code from within itself. */ static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) { struct nd_prefix *pr; struct ifaddr *dstaddr; /* * A link-local address is always a neighbor. * XXX: a link does not necessarily specify a single interface. */ if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { struct sockaddr_in6 sin6_copy; u_int32_t zone; /* * We need sin6_copy since sa6_recoverscope() may modify the * content (XXX). */ sin6_copy = *addr; if (sa6_recoverscope(&sin6_copy)) return (0); /* XXX: should be impossible */ if (in6_setscope(&sin6_copy.sin6_addr, ifp, &zone)) return (0); if (sin6_copy.sin6_scope_id == zone) return (1); else return (0); } /* * If the address matches one of our addresses, * it should be a neighbor. * If the address matches one of our on-link prefixes, it should be a * neighbor. */ LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (pr->ndpr_ifp != ifp) continue; if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) { struct rtentry *rt; /* Always use the default FIB here. */ rt = in6_rtalloc1((struct sockaddr *)&pr->ndpr_prefix, 0, 0, RT_DEFAULT_FIB); if (rt == NULL) continue; /* * This is the case where multiple interfaces * have the same prefix, but only one is installed * into the routing table and that prefix entry * is not the one being examined here. In the case * where RADIX_MPATH is enabled, multiple route * entries (of the same rt_key value) will be * installed because the interface addresses all * differ. */ if (!IN6_ARE_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr)) { RTFREE_LOCKED(rt); continue; } RTFREE_LOCKED(rt); } if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, &addr->sin6_addr, &pr->ndpr_mask)) return (1); } /* * If the address is assigned on the node of the other side of * a p2p interface, the address should be a neighbor. */ dstaddr = ifa_ifwithdstaddr((const struct sockaddr *)addr, RT_ALL_FIBS); if (dstaddr != NULL) { if (dstaddr->ifa_ifp == ifp) { ifa_free(dstaddr); return (1); } ifa_free(dstaddr); } /* * If the default router list is empty, all addresses are regarded * as on-link, and thus, as a neighbor. */ if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && TAILQ_EMPTY(&V_nd_defrouter) && V_nd6_defifindex == ifp->if_index) { return (1); } return (0); } /* * Detect if a given IPv6 address identifies a neighbor on a given link. * XXX: should take care of the destination of a p2p link? */ int nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) { struct llentry *lle; int rc = 0; IF_AFDATA_UNLOCK_ASSERT(ifp); if (nd6_is_new_addr_neighbor(addr, ifp)) return (1); /* * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ IF_AFDATA_RLOCK(ifp); if ((lle = nd6_lookup(&addr->sin6_addr, 0, ifp)) != NULL) { LLE_RUNLOCK(lle); rc = 1; } IF_AFDATA_RUNLOCK(ifp); return (rc); } /* * Free an nd6 llinfo entry. * Since the function would cause significant changes in the kernel, DO NOT * make it global, unless you have a strong reason for the change, and are sure * that the change is safe. * * Set noinline to be dtrace-friendly */ static __noinline void nd6_free(struct llentry *ln, int gc) { struct nd_defrouter *dr; struct ifnet *ifp; LLE_WLOCK_ASSERT(ln); /* * we used to have pfctlinput(PRC_HOSTDEAD) here. * even though it is not harmful, it was not really necessary. */ /* cancel timer */ nd6_llinfo_settimer_locked(ln, -1); ifp = ln->lle_tbl->llt_ifp; if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { dr = defrouter_lookup(&ln->r_l3addr.addr6, ifp); if (dr != NULL && dr->expire && ln->ln_state == ND6_LLINFO_STALE && gc) { /* * If the reason for the deletion is just garbage * collection, and the neighbor is an active default * router, do not delete it. Instead, reset the GC * timer using the router's lifetime. * Simply deleting the entry would affect default * router selection, which is not necessarily a good * thing, especially when we're using router preference * values. * XXX: the check for ln_state would be redundant, * but we intentionally keep it just in case. */ if (dr->expire > time_uptime) nd6_llinfo_settimer_locked(ln, (dr->expire - time_uptime) * hz); else nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); LLE_REMREF(ln); LLE_WUNLOCK(ln); return; } if (dr) { /* * Unreachablity of a router might affect the default * router selection and on-link detection of advertised * prefixes. */ /* * Temporarily fake the state to choose a new default * router and to perform on-link determination of * prefixes correctly. * Below the state will be set correctly, * or the entry itself will be deleted. */ ln->ln_state = ND6_LLINFO_INCOMPLETE; } if (ln->ln_router || dr) { /* * We need to unlock to avoid a LOR with rt6_flush() with the * rnh and for the calls to pfxlist_onlink_check() and * defrouter_select() in the block further down for calls * into nd6_lookup(). We still hold a ref. */ LLE_WUNLOCK(ln); /* * rt6_flush must be called whether or not the neighbor * is in the Default Router List. * See a corresponding comment in nd6_na_input(). */ rt6_flush(&ln->r_l3addr.addr6, ifp); } if (dr) { /* * Since defrouter_select() does not affect the * on-link determination and MIP6 needs the check * before the default router selection, we perform * the check now. */ pfxlist_onlink_check(); /* * Refresh default router list. */ defrouter_select(); } /* * If this entry was added by an on-link redirect, remove the * corresponding host route. */ if (ln->la_flags & LLE_REDIRECT) nd6_free_redirect(ln); if (ln->ln_router || dr) LLE_WLOCK(ln); } /* * Save to unlock. We still hold an extra reference and will not * free(9) in llentry_free() if someone else holds one as well. */ LLE_WUNLOCK(ln); IF_AFDATA_LOCK(ifp); LLE_WLOCK(ln); /* Guard against race with other llentry_free(). */ if (ln->la_flags & LLE_LINKED) { /* Remove callout reference */ LLE_REMREF(ln); lltable_unlink_entry(ln->lle_tbl, ln); } IF_AFDATA_UNLOCK(ifp); llentry_free(ln); } static int nd6_isdynrte(const struct rtentry *rt, void *xap) { if (rt->rt_flags == (RTF_UP | RTF_HOST | RTF_DYNAMIC)) return (1); return (0); } /* * Remove the rtentry for the given llentry, * both of which were installed by a redirect. */ static void nd6_free_redirect(const struct llentry *ln) { int fibnum; struct sockaddr_in6 sin6; struct rt_addrinfo info; lltable_fill_sa_entry(ln, (struct sockaddr *)&sin6); memset(&info, 0, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&sin6; info.rti_filter = nd6_isdynrte; for (fibnum = 0; fibnum < rt_numfibs; fibnum++) rtrequest1_fib(RTM_DELETE, &info, NULL, fibnum); } /* * Rejuvenate this function for routing operations related * processing. */ void nd6_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info) { struct sockaddr_in6 *gateway; struct nd_defrouter *dr; struct ifnet *ifp; gateway = (struct sockaddr_in6 *)rt->rt_gateway; ifp = rt->rt_ifp; switch (req) { case RTM_ADD: break; case RTM_DELETE: if (!ifp) return; /* * Only indirect routes are interesting. */ if ((rt->rt_flags & RTF_GATEWAY) == 0) return; /* * check for default route */ if (IN6_ARE_ADDR_EQUAL(&in6addr_any, &SIN6(rt_key(rt))->sin6_addr)) { dr = defrouter_lookup(&gateway->sin6_addr, ifp); if (dr != NULL) dr->installed = 0; } break; } } int nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) { struct in6_ndireq *ndi = (struct in6_ndireq *)data; struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; struct in6_ndifreq *ndif = (struct in6_ndifreq *)data; int error = 0; if (ifp->if_afdata[AF_INET6] == NULL) return (EPFNOSUPPORT); switch (cmd) { case OSIOCGIFINFO_IN6: #define ND ndi->ndi /* XXX: old ndp(8) assumes a positive value for linkmtu. */ bzero(&ND, sizeof(ND)); ND.linkmtu = IN6_LINKMTU(ifp); ND.maxmtu = ND_IFINFO(ifp)->maxmtu; ND.basereachable = ND_IFINFO(ifp)->basereachable; ND.reachable = ND_IFINFO(ifp)->reachable; ND.retrans = ND_IFINFO(ifp)->retrans; ND.flags = ND_IFINFO(ifp)->flags; ND.recalctm = ND_IFINFO(ifp)->recalctm; ND.chlim = ND_IFINFO(ifp)->chlim; break; case SIOCGIFINFO_IN6: ND = *ND_IFINFO(ifp); break; case SIOCSIFINFO_IN6: /* * used to change host variables from userland. * intented for a use on router to reflect RA configurations. */ /* 0 means 'unspecified' */ if (ND.linkmtu != 0) { if (ND.linkmtu < IPV6_MMTU || ND.linkmtu > IN6_LINKMTU(ifp)) { error = EINVAL; break; } ND_IFINFO(ifp)->linkmtu = ND.linkmtu; } if (ND.basereachable != 0) { int obasereachable = ND_IFINFO(ifp)->basereachable; ND_IFINFO(ifp)->basereachable = ND.basereachable; if (ND.basereachable != obasereachable) ND_IFINFO(ifp)->reachable = ND_COMPUTE_RTIME(ND.basereachable); } if (ND.retrans != 0) ND_IFINFO(ifp)->retrans = ND.retrans; if (ND.chlim != 0) ND_IFINFO(ifp)->chlim = ND.chlim; /* FALLTHROUGH */ case SIOCSIFINFO_FLAGS: { struct ifaddr *ifa; struct in6_ifaddr *ia; if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) && !(ND.flags & ND6_IFF_IFDISABLED)) { /* ifdisabled 1->0 transision */ /* * If the interface is marked as ND6_IFF_IFDISABLED and * has an link-local address with IN6_IFF_DUPLICATED, * do not clear ND6_IFF_IFDISABLED. * See RFC 4862, Section 5.4.5. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if ((ia->ia6_flags & IN6_IFF_DUPLICATED) && IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))) break; } IF_ADDR_RUNLOCK(ifp); if (ifa != NULL) { /* LLA is duplicated. */ ND.flags |= ND6_IFF_IFDISABLED; log(LOG_ERR, "Cannot enable an interface" " with a link-local address marked" " duplicate.\n"); } else { ND_IFINFO(ifp)->flags &= ~ND6_IFF_IFDISABLED; if (ifp->if_flags & IFF_UP) in6_if_up(ifp); } } else if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) && (ND.flags & ND6_IFF_IFDISABLED)) { /* ifdisabled 0->1 transision */ /* Mark all IPv6 address as tentative. */ ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED; if (V_ip6_dad_count > 0 && (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) { IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; ia->ia6_flags |= IN6_IFF_TENTATIVE; } IF_ADDR_RUNLOCK(ifp); } } if (ND.flags & ND6_IFF_AUTO_LINKLOCAL) { if (!(ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL)) { /* auto_linklocal 0->1 transision */ /* If no link-local address on ifp, configure */ ND_IFINFO(ifp)->flags |= ND6_IFF_AUTO_LINKLOCAL; in6_ifattach(ifp, NULL); } else if (!(ND.flags & ND6_IFF_IFDISABLED) && ifp->if_flags & IFF_UP) { /* * When the IF already has * ND6_IFF_AUTO_LINKLOCAL, no link-local * address is assigned, and IFF_UP, try to * assign one. */ IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))) break; } IF_ADDR_RUNLOCK(ifp); if (ifa != NULL) /* No LLA is configured. */ in6_ifattach(ifp, NULL); } } } ND_IFINFO(ifp)->flags = ND.flags; break; #undef ND case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ /* sync kernel routing table with the default router list */ defrouter_reset(); defrouter_select(); break; case SIOCSPFXFLUSH_IN6: { /* flush all the prefix advertised by routers */ struct nd_prefix *pr, *next; LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, next) { struct in6_ifaddr *ia, *ia_next; if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) continue; /* XXX */ /* do we really have to remove addresses as well? */ /* XXXRW: in6_ifaddrhead locking. */ TAILQ_FOREACH_SAFE(ia, &V_in6_ifaddrhead, ia_link, ia_next) { if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; if (ia->ia6_ndpr == pr) in6_purgeaddr(&ia->ia_ifa); } prelist_remove(pr); } break; } case SIOCSRTRFLUSH_IN6: { /* flush all the default routers */ struct nd_defrouter *dr, *next; defrouter_reset(); TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, next) { defrtrlist_del(dr); } defrouter_select(); break; } case SIOCGNBRINFO_IN6: { struct llentry *ln; struct in6_addr nb_addr = nbi->addr; /* make local for safety */ if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0) return (error); IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(&nb_addr, 0, ifp); IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) { error = EINVAL; break; } nbi->state = ln->ln_state; nbi->asked = ln->la_asked; nbi->isrouter = ln->ln_router; if (ln->la_expire == 0) nbi->expire = 0; else nbi->expire = ln->la_expire + ln->lle_remtime / hz + (time_second - time_uptime); LLE_RUNLOCK(ln); break; } case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ ndif->ifindex = V_nd6_defifindex; break; case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ return (nd6_setdefaultiface(ndif->ifindex)); } return (error); } /* * Calculates new isRouter value based on provided parameters and * returns it. */ static int nd6_is_router(int type, int code, int is_new, int old_addr, int new_addr, int ln_router) { /* * ICMP6 type dependent behavior. * * NS: clear IsRouter if new entry * RS: clear IsRouter * RA: set IsRouter if there's lladdr * redir: clear IsRouter if new entry * * RA case, (1): * The spec says that we must set IsRouter in the following cases: * - If lladdr exist, set IsRouter. This means (1-5). * - If it is old entry (!newentry), set IsRouter. This means (7). * So, based on the spec, in (1-5) and (7) cases we must set IsRouter. * A quetion arises for (1) case. (1) case has no lladdr in the * neighbor cache, this is similar to (6). * This case is rare but we figured that we MUST NOT set IsRouter. * * is_new old_addr new_addr NS RS RA redir * D R * 0 n n (1) c ? s * 0 y n (2) c s s * 0 n y (3) c s s * 0 y y (4) c s s * 0 y y (5) c s s * 1 -- n (6) c c c s * 1 -- y (7) c c s c s * * (c=clear s=set) */ switch (type & 0xff) { case ND_NEIGHBOR_SOLICIT: /* * New entry must have is_router flag cleared. */ if (is_new) /* (6-7) */ ln_router = 0; break; case ND_REDIRECT: /* * If the icmp is a redirect to a better router, always set the * is_router flag. Otherwise, if the entry is newly created, * clear the flag. [RFC 2461, sec 8.3] */ if (code == ND_REDIRECT_ROUTER) ln_router = 1; else { if (is_new) /* (6-7) */ ln_router = 0; } break; case ND_ROUTER_SOLICIT: /* * is_router flag must always be cleared. */ ln_router = 0; break; case ND_ROUTER_ADVERT: /* * Mark an entry with lladdr as a router. */ if ((!is_new && (old_addr || new_addr)) || /* (2-5) */ (is_new && new_addr)) { /* (7) */ ln_router = 1; } break; } return (ln_router); } /* * Create neighbor cache entry and cache link-layer address, * on reception of inbound ND6 packets. (RS/RA/NS/redirect) * * type - ICMP6 type * code - type dependent information * */ void nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, int lladdrlen, int type, int code) { struct llentry *ln = NULL, *ln_tmp; int is_newentry; int do_update; int olladdr; int llchange; int flags; uint16_t router = 0; struct sockaddr_in6 sin6; struct mbuf *chain = NULL; + u_char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; IF_AFDATA_UNLOCK_ASSERT(ifp); KASSERT(ifp != NULL, ("%s: ifp == NULL", __func__)); KASSERT(from != NULL, ("%s: from == NULL", __func__)); /* nothing must be updated for unspecified address */ if (IN6_IS_ADDR_UNSPECIFIED(from)) return; /* * Validation about ifp->if_addrlen and lladdrlen must be done in * the caller. * * XXX If the link does not have link-layer adderss, what should * we do? (ifp->if_addrlen == 0) * Spec says nothing in sections for RA, RS and NA. There's small * description on it in NS section (RFC 2461 7.2.3). */ flags = lladdr ? LLE_EXCLUSIVE : 0; IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(from, flags, ifp); IF_AFDATA_RUNLOCK(ifp); is_newentry = 0; if (ln == NULL) { flags |= LLE_EXCLUSIVE; ln = nd6_alloc(from, 0, ifp); if (ln == NULL) return; /* * Since we already know all the data for the new entry, * fill it before insertion. */ - if (lladdr != NULL) - lltable_set_entry_addr(ifp, ln, lladdr); + if (lladdr != NULL) { + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return; + lltable_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, + lladdr_off); + } + IF_AFDATA_WLOCK(ifp); LLE_WLOCK(ln); /* Prefer any existing lle over newly-created one */ ln_tmp = nd6_lookup(from, LLE_EXCLUSIVE, ifp); if (ln_tmp == NULL) lltable_link_entry(LLTABLE6(ifp), ln); IF_AFDATA_WUNLOCK(ifp); if (ln_tmp == NULL) { /* No existing lle, mark as new entry (6,7) */ is_newentry = 1; nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); if (lladdr != NULL) /* (7) */ EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); } else { lltable_free_entry(LLTABLE6(ifp), ln); ln = ln_tmp; ln_tmp = NULL; } } /* do nothing if static ndp is set */ if ((ln->la_flags & LLE_STATIC)) { if (flags & LLE_EXCLUSIVE) LLE_WUNLOCK(ln); else LLE_RUNLOCK(ln); return; } olladdr = (ln->la_flags & LLE_VALID) ? 1 : 0; if (olladdr && lladdr) { - llchange = bcmp(lladdr, &ln->ll_addr, + llchange = bcmp(lladdr, ln->ll_addr, ifp->if_addrlen); } else if (!olladdr && lladdr) llchange = 1; else llchange = 0; /* * newentry olladdr lladdr llchange (*=record) * 0 n n -- (1) * 0 y n -- (2) * 0 n y y (3) * STALE * 0 y y n (4) * * 0 y y y (5) * STALE * 1 -- n -- (6) NOSTATE(= PASSIVE) * 1 -- y -- (7) * STALE */ do_update = 0; if (is_newentry == 0 && llchange != 0) { do_update = 1; /* (3,5) */ /* * Record source link-layer address * XXX is it dependent to ifp->if_type? */ - if (lltable_try_set_entry_addr(ifp, ln, lladdr) == 0) { + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return; + + if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, + lladdr_off) == 0) { /* Entry was deleted */ return; } nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); if (ln->la_hold != NULL) nd6_grab_holdchain(ln, &chain, &sin6); } /* Calculates new router status */ router = nd6_is_router(type, code, is_newentry, olladdr, lladdr != NULL ? 1 : 0, ln->ln_router); ln->ln_router = router; /* Mark non-router redirects with special flag */ if ((type & 0xFF) == ND_REDIRECT && code != ND_REDIRECT_ROUTER) ln->la_flags |= LLE_REDIRECT; if (flags & LLE_EXCLUSIVE) LLE_WUNLOCK(ln); else LLE_RUNLOCK(ln); if (chain != NULL) nd6_flush_holdchain(ifp, ifp, chain, &sin6); /* * When the link-layer address of a router changes, select the * best router again. In particular, when the neighbor entry is newly * created, it might affect the selection policy. * Question: can we restrict the first condition to the "is_newentry" * case? * XXX: when we hear an RA from a new router with the link-layer * address option, defrouter_select() is called twice, since * defrtrlist_update called the function as well. However, I believe * we can compromise the overhead, since it only happens the first * time. * XXX: although defrouter_select() should not have a bad effect * for those are not autoconfigured hosts, we explicitly avoid such * cases for safety. */ if ((do_update || is_newentry) && router && ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) { /* * guaranteed recursion */ defrouter_select(); } } static void nd6_slowtimo(void *arg) { CURVNET_SET((struct vnet *) arg); struct nd_ifinfo *nd6if; struct ifnet *ifp; callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, curvnet); IFNET_RLOCK_NOSLEEP(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (ifp->if_afdata[AF_INET6] == NULL) continue; nd6if = ND_IFINFO(ifp); if (nd6if->basereachable && /* already initialized */ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { /* * Since reachable time rarely changes by router * advertisements, we SHOULD insure that a new random * value gets recomputed at least once every few hours. * (RFC 2461, 6.3.4) */ nd6if->recalctm = V_nd6_recalc_reachtm_interval; nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); } } IFNET_RUNLOCK_NOSLEEP(); CURVNET_RESTORE(); } void nd6_grab_holdchain(struct llentry *ln, struct mbuf **chain, struct sockaddr_in6 *sin6) { LLE_WLOCK_ASSERT(ln); *chain = ln->la_hold; ln->la_hold = NULL; lltable_fill_sa_entry(ln, (struct sockaddr *)sin6); if (ln->ln_state == ND6_LLINFO_STALE) { /* * The first time we send a packet to a * neighbor whose entry is STALE, we have * to change the state to DELAY and a sets * a timer to expire in DELAY_FIRST_PROBE_TIME * seconds to ensure do neighbor unreachability * detection on expiration. * (RFC 2461 7.3.3) */ nd6_llinfo_setstate(ln, ND6_LLINFO_DELAY); } } int nd6_output_ifp(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, struct sockaddr_in6 *dst, struct route *ro) { int error; int ip6len; struct ip6_hdr *ip6; struct m_tag *mtag; #ifdef MAC mac_netinet6_nd6_send(ifp, m); #endif /* * If called from nd6_ns_output() (NS), nd6_na_output() (NA), * icmp6_redirect_output() (REDIRECT) or from rip6_output() (RS, RA * as handled by rtsol and rtadvd), mbufs will be tagged for SeND * to be diverted to user space. When re-injected into the kernel, * send_output() will directly dispatch them to the outgoing interface. */ if (send_sendso_input_hook != NULL) { mtag = m_tag_find(m, PACKET_TAG_ND_OUTGOING, NULL); if (mtag != NULL) { ip6 = mtod(m, struct ip6_hdr *); ip6len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); /* Use the SEND socket */ error = send_sendso_input_hook(m, ifp, SND_OUT, ip6len); /* -1 == no app on SEND socket */ if (error == 0 || error != -1) return (error); } } m_clrprotoflags(m); /* Avoid confusing lower layers. */ IP_PROBE(send, NULL, NULL, mtod(m, struct ip6_hdr *), ifp, NULL, mtod(m, struct ip6_hdr *)); if ((ifp->if_flags & IFF_LOOPBACK) == 0) origifp = ifp; error = (*ifp->if_output)(origifp, m, (struct sockaddr *)dst, ro); return (error); } /* - * Do L2 address resolution for @sa_dst address. Stores found - * address in @desten buffer. Copy of lle ln_flags can be also + * Lookup link headerfor @sa_dst address. Stores found + * data in @desten buffer. Copy of lle ln_flags can be also * saved in @pflags if @pflags is non-NULL. * * If destination LLE does not exists or lle state modification * is required, call "slow" version. * * Return values: * - 0 on success (address copied to buffer). * - EWOULDBLOCK (no local error, but address is still unresolved) * - other errors (alloc failure, etc) */ int nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m, const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags) { struct llentry *ln = NULL; const struct sockaddr_in6 *dst6; if (pflags != NULL) *pflags = 0; dst6 = (const struct sockaddr_in6 *)sa_dst; /* discard the packet if IPv6 operation is disabled on the interface */ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { m_freem(m); return (ENETDOWN); /* better error? */ } if (m != NULL && m->m_flags & M_MCAST) { switch (ifp->if_type) { case IFT_ETHER: case IFT_FDDI: case IFT_L2VLAN: case IFT_IEEE80211: case IFT_BRIDGE: case IFT_ISO88025: ETHER_MAP_IPV6_MULTICAST(&dst6->sin6_addr, desten); return (0); default: m_freem(m); return (EAFNOSUPPORT); } } IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(&dst6->sin6_addr, LLE_UNLOCKED, ifp); if (ln != NULL && (ln->r_flags & RLLE_VALID) != 0) { /* Entry found, let's copy lle info */ - bcopy(&ln->ll_addr, desten, ifp->if_addrlen); + bcopy(ln->r_linkdata, desten, ln->r_hdrlen); if (pflags != NULL) *pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR); /* Check if we have feedback request from nd6 timer */ if (ln->r_skip_req != 0) { LLE_REQ_LOCK(ln); ln->r_skip_req = 0; /* Notify that entry was used */ ln->lle_hittime = time_uptime; LLE_REQ_UNLOCK(ln); } IF_AFDATA_RUNLOCK(ifp); return (0); } IF_AFDATA_RUNLOCK(ifp); - return (nd6_resolve_slow(ifp, m, dst6, desten, pflags)); + return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags)); } /* * Do L2 address resolution for @sa_dst address. Stores found * address in @desten buffer. Copy of lle ln_flags can be also * saved in @pflags if @pflags is non-NULL. * * Heavy version. * Function assume that destination LLE does not exist, * is invalid or stale, so LLE_EXCLUSIVE lock needs to be acquired. * * Set noinline to be dtrace-friendly */ static __noinline int -nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m, +nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m, const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags) { struct llentry *lle = NULL, *lle_tmp; struct in6_addr *psrc, src; - int send_ns; + int send_ns, ll_len; + char *lladdr; /* * Address resolution or Neighbor Unreachability Detection * for the next hop. * At this point, the destination of the packet must be a unicast * or an anycast address(i.e. not a multicast). */ if (lle == NULL) { IF_AFDATA_RLOCK(ifp); lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp); IF_AFDATA_RUNLOCK(ifp); if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) { /* * Since nd6_is_addr_neighbor() internally calls nd6_lookup(), * the condition below is not very efficient. But we believe * it is tolerable, because this should be a rare case. */ lle = nd6_alloc(&dst->sin6_addr, 0, ifp); if (lle == NULL) { char ip6buf[INET6_ADDRSTRLEN]; log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " "(ln=%p)\n", ip6_sprintf(ip6buf, &dst->sin6_addr), lle); m_freem(m); return (ENOBUFS); } IF_AFDATA_WLOCK(ifp); LLE_WLOCK(lle); /* Prefer any existing entry over newly-created one */ lle_tmp = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp); if (lle_tmp == NULL) lltable_link_entry(LLTABLE6(ifp), lle); IF_AFDATA_WUNLOCK(ifp); if (lle_tmp != NULL) { lltable_free_entry(LLTABLE6(ifp), lle); lle = lle_tmp; lle_tmp = NULL; } } } if (lle == NULL) { if (!(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) { m_freem(m); return (ENOBUFS); } if (m != NULL) m_freem(m); return (ENOBUFS); } LLE_WLOCK_ASSERT(lle); /* * The first time we send a packet to a neighbor whose entry is * STALE, we have to change the state to DELAY and a sets a timer to * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do * neighbor unreachability detection on expiration. * (RFC 2461 7.3.3) */ if (lle->ln_state == ND6_LLINFO_STALE) nd6_llinfo_setstate(lle, ND6_LLINFO_DELAY); /* * If the neighbor cache entry has a state other than INCOMPLETE * (i.e. its link-layer address is already resolved), just * send the packet. */ if (lle->ln_state > ND6_LLINFO_INCOMPLETE) { - bcopy(&lle->ll_addr, desten, ifp->if_addrlen); + if (flags & LLE_ADDRONLY) { + lladdr = lle->ll_addr; + ll_len = ifp->if_addrlen; + } else { + lladdr = lle->r_linkdata; + ll_len = lle->r_hdrlen; + } + bcopy(lladdr, desten, ll_len); if (pflags != NULL) *pflags = lle->la_flags; LLE_WUNLOCK(lle); return (0); } /* * There is a neighbor cache entry, but no ethernet address * response yet. Append this latest packet to the end of the * packet queue in the mbuf, unless the number of the packet * does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen, * the oldest packet in the queue will be removed. */ if (lle->la_hold != NULL) { struct mbuf *m_hold; int i; i = 0; for (m_hold = lle->la_hold; m_hold; m_hold = m_hold->m_nextpkt){ i++; if (m_hold->m_nextpkt == NULL) { m_hold->m_nextpkt = m; break; } } while (i >= V_nd6_maxqueuelen) { m_hold = lle->la_hold; lle->la_hold = lle->la_hold->m_nextpkt; m_freem(m_hold); i--; } } else { lle->la_hold = m; } /* * If there has been no NS for the neighbor after entering the * INCOMPLETE state, send the first solicitation. * Note that for newly-created lle la_asked will be 0, * so we will transition from ND6_LLINFO_NOSTATE to * ND6_LLINFO_INCOMPLETE state here. */ psrc = NULL; send_ns = 0; if (lle->la_asked == 0) { lle->la_asked++; send_ns = 1; psrc = nd6_llinfo_get_holdsrc(lle, &src); nd6_llinfo_setstate(lle, ND6_LLINFO_INCOMPLETE); } LLE_WUNLOCK(lle); if (send_ns != 0) nd6_ns_output(ifp, psrc, NULL, &dst->sin6_addr, NULL); return (EWOULDBLOCK); } +/* + * Do L2 address resolution for @sa_dst address. Stores found + * address in @desten buffer. Copy of lle ln_flags can be also + * saved in @pflags if @pflags is non-NULL. + * + * Return values: + * - 0 on success (address copied to buffer). + * - EWOULDBLOCK (no local error, but address is still unresolved) + * - other errors (alloc failure, etc) + */ +int +nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, + char *desten, uint32_t *pflags) +{ + int error; + + flags |= LLE_ADDRONLY; + error = nd6_resolve_slow(ifp, flags, NULL, + (const struct sockaddr_in6 *)dst, desten, pflags); + return (error); +} int nd6_flush_holdchain(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *chain, struct sockaddr_in6 *dst) { struct mbuf *m, *m_head; struct ifnet *outifp; int error = 0; m_head = chain; if ((ifp->if_flags & IFF_LOOPBACK) != 0) outifp = origifp; else outifp = ifp; while (m_head) { m = m_head; m_head = m_head->m_nextpkt; error = nd6_output_ifp(ifp, origifp, m, dst, NULL); } /* * XXX * note that intermediate errors are blindly ignored */ return (error); } static int nd6_need_cache(struct ifnet *ifp) { /* * XXX: we currently do not make neighbor cache on any interface * other than ARCnet, Ethernet, FDDI and GIF. * * RFC2893 says: * - unidirectional tunnels needs no ND */ switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: case IFT_IEEE1394: case IFT_L2VLAN: case IFT_IEEE80211: case IFT_INFINIBAND: case IFT_BRIDGE: case IFT_PROPVIRTUAL: return (1); default: return (0); } } /* * Add pernament ND6 link-layer record for given * interface address. * * Very similar to IPv4 arp_ifinit(), but: * 1) IPv6 DAD is performed in different place * 2) It is called by IPv6 protocol stack in contrast to * arp_ifinit() which is typically called in SIOCSIFADDR * driver ioctl handler. * */ int nd6_add_ifa_lle(struct in6_ifaddr *ia) { struct ifnet *ifp; struct llentry *ln, *ln_tmp; struct sockaddr *dst; ifp = ia->ia_ifa.ifa_ifp; if (nd6_need_cache(ifp) == 0) return (0); ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; dst = (struct sockaddr *)&ia->ia_addr; ln = lltable_alloc_entry(LLTABLE6(ifp), LLE_IFADDR, dst); if (ln == NULL) return (ENOBUFS); IF_AFDATA_WLOCK(ifp); LLE_WLOCK(ln); /* Unlink any entry if exists */ ln_tmp = lla_lookup(LLTABLE6(ifp), LLE_EXCLUSIVE, dst); if (ln_tmp != NULL) lltable_unlink_entry(LLTABLE6(ifp), ln_tmp); lltable_link_entry(LLTABLE6(ifp), ln); IF_AFDATA_WUNLOCK(ifp); if (ln_tmp != NULL) EVENTHANDLER_INVOKE(lle_event, ln_tmp, LLENTRY_EXPIRED); EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); LLE_WUNLOCK(ln); if (ln_tmp != NULL) llentry_free(ln_tmp); return (0); } /* * Removes either all lle entries for given @ia, or lle * corresponding to @ia address. */ void nd6_rem_ifa_lle(struct in6_ifaddr *ia, int all) { struct sockaddr_in6 mask, addr; struct sockaddr *saddr, *smask; struct ifnet *ifp; ifp = ia->ia_ifa.ifa_ifp; memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); saddr = (struct sockaddr *)&addr; smask = (struct sockaddr *)&mask; if (all != 0) lltable_prefix_free(AF_INET6, saddr, smask, LLE_STATIC); else lltable_delete_addr(LLTABLE6(ifp), LLE_IFADDR, saddr); } static void clear_llinfo_pqueue(struct llentry *ln) { struct mbuf *m_hold, *m_hold_next; for (m_hold = ln->la_hold; m_hold; m_hold = m_hold_next) { m_hold_next = m_hold->m_nextpkt; m_freem(m_hold); } ln->la_hold = NULL; return; } static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS); static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS); #ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet6_icmp6); #endif SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist, CTLFLAG_RD, nd6_sysctl_drlist, ""); SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, CTLFLAG_RD, nd6_sysctl_prlist, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_maxqueuelen), 1, ""); SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, nd6_gctimer, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_gctimer), (60 * 60 * 24), ""); static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) { struct in6_defrouter d; struct nd_defrouter *dr; int error; if (req->newptr) return (EPERM); bzero(&d, sizeof(d)); d.rtaddr.sin6_family = AF_INET6; d.rtaddr.sin6_len = sizeof(d.rtaddr); /* * XXX locking */ TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { d.rtaddr.sin6_addr = dr->rtaddr; error = sa6_recoverscope(&d.rtaddr); if (error != 0) return (error); d.flags = dr->flags; d.rtlifetime = dr->rtlifetime; d.expire = dr->expire + (time_second - time_uptime); d.if_index = dr->ifp->if_index; error = SYSCTL_OUT(req, &d, sizeof(d)); if (error != 0) return (error); } return (0); } static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS) { struct in6_prefix p; struct sockaddr_in6 s6; struct nd_prefix *pr; struct nd_pfxrouter *pfr; time_t maxexpire; int error; char ip6buf[INET6_ADDRSTRLEN]; if (req->newptr) return (EPERM); bzero(&p, sizeof(p)); p.origin = PR_ORIG_RA; bzero(&s6, sizeof(s6)); s6.sin6_family = AF_INET6; s6.sin6_len = sizeof(s6); /* * XXX locking */ LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { p.prefix = pr->ndpr_prefix; if (sa6_recoverscope(&p.prefix)) { log(LOG_ERR, "scope error in prefix list (%s)\n", ip6_sprintf(ip6buf, &p.prefix.sin6_addr)); /* XXX: press on... */ } p.raflags = pr->ndpr_raf; p.prefixlen = pr->ndpr_plen; p.vltime = pr->ndpr_vltime; p.pltime = pr->ndpr_pltime; p.if_index = pr->ndpr_ifp->if_index; if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) p.expire = 0; else { /* XXX: we assume time_t is signed. */ maxexpire = (-1) & ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); if (pr->ndpr_vltime < maxexpire - pr->ndpr_lastupdate) p.expire = pr->ndpr_lastupdate + pr->ndpr_vltime + (time_second - time_uptime); else p.expire = maxexpire; } p.refcnt = pr->ndpr_refcnt; p.flags = pr->ndpr_stateflags; p.advrtrs = 0; LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) p.advrtrs++; error = SYSCTL_OUT(req, &p, sizeof(p)); if (error != 0) return (error); LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { s6.sin6_addr = pfr->router->rtaddr; if (sa6_recoverscope(&s6)) log(LOG_ERR, "scope error in prefix list (%s)\n", ip6_sprintf(ip6buf, &pfr->router->rtaddr)); error = SYSCTL_OUT(req, &s6, sizeof(s6)); if (error != 0) return (error); } } return (0); } Index: projects/clang380-import/sys/netinet6/nd6.h =================================================================== --- projects/clang380-import/sys/netinet6/nd6.h (revision 293015) +++ projects/clang380-import/sys/netinet6/nd6.h (revision 293016) @@ -1,458 +1,460 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: nd6.h,v 1.76 2001/12/18 02:10:31 itojun Exp $ * $FreeBSD$ */ #ifndef _NETINET6_ND6_H_ #define _NETINET6_ND6_H_ /* see net/route.h, or net/if_inarp.h */ #ifndef RTF_ANNOUNCE #define RTF_ANNOUNCE RTF_PROTO2 #endif #include #include struct llentry; #define ND6_LLINFO_NOSTATE -2 /* * We don't need the WAITDELETE state any more, but we keep the definition * in a comment line instead of removing it. This is necessary to avoid * unintentionally reusing the value for another purpose, which might * affect backward compatibility with old applications. * (20000711 jinmei@kame.net) */ /* #define ND6_LLINFO_WAITDELETE -1 */ #define ND6_LLINFO_INCOMPLETE 0 #define ND6_LLINFO_REACHABLE 1 #define ND6_LLINFO_STALE 2 #define ND6_LLINFO_DELAY 3 #define ND6_LLINFO_PROBE 4 #define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) #define ND6_LLINFO_PERMANENT(n) (((n)->la_expire == 0) && ((n)->ln_state > ND6_LLINFO_INCOMPLETE)) struct nd_ifinfo { u_int32_t linkmtu; /* LinkMTU */ u_int32_t maxmtu; /* Upper bound of LinkMTU */ u_int32_t basereachable; /* BaseReachableTime */ u_int32_t reachable; /* Reachable Time */ u_int32_t retrans; /* Retrans Timer */ u_int32_t flags; /* Flags */ int recalctm; /* BaseReacable re-calculation timer */ u_int8_t chlim; /* CurHopLimit */ u_int8_t initialized; /* Flag to see the entry is initialized */ /* the following 3 members are for privacy extension for addrconf */ u_int8_t randomseed0[8]; /* upper 64 bits of MD5 digest */ u_int8_t randomseed1[8]; /* lower 64 bits (usually the EUI64 IFID) */ u_int8_t randomid[8]; /* current random ID */ }; #define ND6_IFF_PERFORMNUD 0x1 #define ND6_IFF_ACCEPT_RTADV 0x2 #define ND6_IFF_PREFER_SOURCE 0x4 /* Not used in FreeBSD. */ #define ND6_IFF_IFDISABLED 0x8 /* IPv6 operation is disabled due to * DAD failure. (XXX: not ND-specific) */ #define ND6_IFF_DONT_SET_IFROUTE 0x10 #define ND6_IFF_AUTO_LINKLOCAL 0x20 #define ND6_IFF_NO_RADR 0x40 #define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */ #define ND6_IFF_NO_DAD 0x100 #ifdef _KERNEL #define ND_IFINFO(ifp) \ (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->nd_ifinfo) #define IN6_LINKMTU(ifp) \ ((ND_IFINFO(ifp)->linkmtu && ND_IFINFO(ifp)->linkmtu < (ifp)->if_mtu) \ ? ND_IFINFO(ifp)->linkmtu \ : ((ND_IFINFO(ifp)->maxmtu && ND_IFINFO(ifp)->maxmtu < (ifp)->if_mtu) \ ? ND_IFINFO(ifp)->maxmtu : (ifp)->if_mtu)) #endif struct in6_nbrinfo { char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ struct in6_addr addr; /* IPv6 address of the neighbor */ long asked; /* number of queries already sent for this addr */ int isrouter; /* if it acts as a router */ int state; /* reachability state */ int expire; /* lifetime for NDP state transition */ }; #define DRLSTSIZ 10 #define PRLSTSIZ 10 struct in6_drlist { char ifname[IFNAMSIZ]; struct { struct in6_addr rtaddr; u_char flags; u_short rtlifetime; u_long expire; u_short if_index; } defrouter[DRLSTSIZ]; }; struct in6_defrouter { struct sockaddr_in6 rtaddr; u_char flags; u_short rtlifetime; u_long expire; u_short if_index; }; #ifdef _KERNEL struct in6_oprlist { char ifname[IFNAMSIZ]; struct { struct in6_addr prefix; struct prf_ra raflags; u_char prefixlen; u_char origin; u_long vltime; u_long pltime; u_long expire; u_short if_index; u_short advrtrs; /* number of advertisement routers */ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ } prefix[PRLSTSIZ]; }; #endif struct in6_prlist { char ifname[IFNAMSIZ]; struct { struct in6_addr prefix; struct prf_ra raflags; u_char prefixlen; u_char origin; u_int32_t vltime; u_int32_t pltime; time_t expire; u_short if_index; u_short advrtrs; /* number of advertisement routers */ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ } prefix[PRLSTSIZ]; }; struct in6_prefix { struct sockaddr_in6 prefix; struct prf_ra raflags; u_char prefixlen; u_char origin; u_int32_t vltime; u_int32_t pltime; time_t expire; u_int32_t flags; int refcnt; u_short if_index; u_short advrtrs; /* number of advertisement routers */ /* struct sockaddr_in6 advrtr[] */ }; #ifdef _KERNEL struct in6_ondireq { char ifname[IFNAMSIZ]; struct { u_int32_t linkmtu; /* LinkMTU */ u_int32_t maxmtu; /* Upper bound of LinkMTU */ u_int32_t basereachable; /* BaseReachableTime */ u_int32_t reachable; /* Reachable Time */ u_int32_t retrans; /* Retrans Timer */ u_int32_t flags; /* Flags */ int recalctm; /* BaseReacable re-calculation timer */ u_int8_t chlim; /* CurHopLimit */ u_int8_t receivedra; } ndi; }; #endif struct in6_ndireq { char ifname[IFNAMSIZ]; struct nd_ifinfo ndi; }; struct in6_ndifreq { char ifname[IFNAMSIZ]; u_long ifindex; }; /* Prefix status */ #define NDPRF_ONLINK 0x1 #define NDPRF_DETACHED 0x2 /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */ #define RTR_SOLICITATION_INTERVAL 4 /* 4sec */ #define MAX_RTR_SOLICITATIONS 3 #define ND6_INFINITE_LIFETIME 0xffffffff #ifdef _KERNEL /* node constants */ #define MAX_REACHABLE_TIME 3600000 /* msec */ #define REACHABLE_TIME 30000 /* msec */ #define RETRANS_TIMER 1000 /* msec */ #define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ #define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ #define DEF_TEMP_VALID_LIFETIME 604800 /* 1 week */ #define DEF_TEMP_PREFERRED_LIFETIME 86400 /* 1 day */ #define TEMPADDR_REGEN_ADVANCE 5 /* sec */ #define MAX_TEMP_DESYNC_FACTOR 600 /* 10 min */ #define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (arc4random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) TAILQ_HEAD(nd_drhead, nd_defrouter); struct nd_defrouter { TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; u_char flags; /* flags on RA message */ u_short rtlifetime; u_long expire; struct ifnet *ifp; int installed; /* is installed into kernel routing table */ }; struct nd_prefixctl { struct ifnet *ndpr_ifp; /* prefix */ struct sockaddr_in6 ndpr_prefix; u_char ndpr_plen; u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */ struct prf_ra ndpr_flags; }; struct nd_prefix { struct ifnet *ndpr_ifp; LIST_ENTRY(nd_prefix) ndpr_entry; struct sockaddr_in6 ndpr_prefix; /* prefix */ struct in6_addr ndpr_mask; /* netmask derived from the prefix */ u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */ time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */ time_t ndpr_lastupdate; /* reception time of last advertisement */ struct prf_ra ndpr_flags; u_int32_t ndpr_stateflags; /* actual state flags */ /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; int ndpr_refcnt; /* reference couter from addresses */ }; #define ndpr_raf ndpr_flags #define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_auto ndpr_flags.autonomous #define ndpr_raf_router ndpr_flags.router /* * Message format for use in obtaining information about prefixes * from inet6 sysctl function */ struct inet6_ndpr_msghdr { u_short inpm_msglen; /* to skip over non-understood messages */ u_char inpm_version; /* future binary compatibility */ u_char inpm_type; /* message type */ struct in6_addr inpm_prefix; u_long prm_vltim; u_long prm_pltime; u_long prm_expire; u_long prm_preferred; struct in6_prflags prm_flags; u_short prm_index; /* index for associated ifp */ u_char prm_plen; /* length of prefix in bits */ }; #define prm_raf_onlink prm_flags.prf_ra.onlink #define prm_raf_auto prm_flags.prf_ra.autonomous #define prm_statef_onlink prm_flags.prf_state.onlink #define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid #define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; struct nd_defrouter *router; }; LIST_HEAD(nd_prhead, nd_prefix); /* nd6.c */ VNET_DECLARE(int, nd6_prune); VNET_DECLARE(int, nd6_delay); VNET_DECLARE(int, nd6_umaxtries); VNET_DECLARE(int, nd6_mmaxtries); VNET_DECLARE(int, nd6_useloopback); VNET_DECLARE(int, nd6_maxnudhint); VNET_DECLARE(int, nd6_gctimer); VNET_DECLARE(struct nd_drhead, nd_defrouter); VNET_DECLARE(struct nd_prhead, nd_prefix); VNET_DECLARE(int, nd6_debug); VNET_DECLARE(int, nd6_onlink_ns_rfc4861); #define V_nd6_prune VNET(nd6_prune) #define V_nd6_delay VNET(nd6_delay) #define V_nd6_umaxtries VNET(nd6_umaxtries) #define V_nd6_mmaxtries VNET(nd6_mmaxtries) #define V_nd6_useloopback VNET(nd6_useloopback) #define V_nd6_maxnudhint VNET(nd6_maxnudhint) #define V_nd6_gctimer VNET(nd6_gctimer) #define V_nd_defrouter VNET(nd_defrouter) #define V_nd_prefix VNET(nd_prefix) #define V_nd6_debug VNET(nd6_debug) #define V_nd6_onlink_ns_rfc4861 VNET(nd6_onlink_ns_rfc4861) #define nd6log(x) do { if (V_nd6_debug) log x; } while (/*CONSTCOND*/ 0) VNET_DECLARE(struct callout, nd6_timer_ch); #define V_nd6_timer_ch VNET(nd6_timer_ch) /* nd6_rtr.c */ VNET_DECLARE(int, nd6_defifindex); VNET_DECLARE(int, ip6_desync_factor); /* seconds */ VNET_DECLARE(u_int32_t, ip6_temp_preferred_lifetime); /* seconds */ VNET_DECLARE(u_int32_t, ip6_temp_valid_lifetime); /* seconds */ VNET_DECLARE(int, ip6_temp_regen_advance); /* seconds */ #define V_nd6_defifindex VNET(nd6_defifindex) #define V_ip6_desync_factor VNET(ip6_desync_factor) #define V_ip6_temp_preferred_lifetime VNET(ip6_temp_preferred_lifetime) #define V_ip6_temp_valid_lifetime VNET(ip6_temp_valid_lifetime) #define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance) union nd_opts { struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */ struct { struct nd_opt_hdr *zero; struct nd_opt_hdr *src_lladdr; struct nd_opt_hdr *tgt_lladdr; struct nd_opt_prefix_info *pi_beg; /* multiple opts, start */ struct nd_opt_rd_hdr *rh; struct nd_opt_mtu *mtu; struct nd_opt_hdr *__res6; struct nd_opt_hdr *__res7; struct nd_opt_hdr *__res8; struct nd_opt_hdr *__res9; struct nd_opt_hdr *__res10; struct nd_opt_hdr *__res11; struct nd_opt_hdr *__res12; struct nd_opt_hdr *__res13; struct nd_opt_nonce *nonce; struct nd_opt_hdr *__res15; struct nd_opt_hdr *search; /* multiple opts */ struct nd_opt_hdr *last; /* multiple opts */ int done; struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ } nd_opt_each; }; #define nd_opts_src_lladdr nd_opt_each.src_lladdr #define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr #define nd_opts_pi nd_opt_each.pi_beg #define nd_opts_pi_end nd_opt_each.pi_end #define nd_opts_rh nd_opt_each.rh #define nd_opts_mtu nd_opt_each.mtu #define nd_opts_nonce nd_opt_each.nonce #define nd_opts_search nd_opt_each.search #define nd_opts_last nd_opt_each.last #define nd_opts_done nd_opt_each.done /* XXX: need nd6_var.h?? */ /* nd6.c */ void nd6_init(void); #ifdef VIMAGE void nd6_destroy(void); #endif struct nd_ifinfo *nd6_ifattach(struct ifnet *); void nd6_ifdetach(struct nd_ifinfo *); int nd6_is_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); void nd6_option_init(void *, int, union nd_opts *); struct nd_opt_hdr *nd6_option(union nd_opts *); int nd6_options(union nd_opts *); struct llentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *); struct llentry *nd6_alloc(const struct in6_addr *, int, struct ifnet *); void nd6_setmtu(struct ifnet *); void nd6_llinfo_setstate(struct llentry *lle, int newstate); void nd6_timer(void *); void nd6_purge(struct ifnet *); +int nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, + char *desten, uint32_t *pflags); int nd6_resolve(struct ifnet *, int, struct mbuf *, const struct sockaddr *, u_char *, uint32_t *); int nd6_ioctl(u_long, caddr_t, struct ifnet *); void nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); void nd6_grab_holdchain(struct llentry *, struct mbuf **, struct sockaddr_in6 *); int nd6_flush_holdchain(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *); int nd6_add_ifa_lle(struct in6_ifaddr *); void nd6_rem_ifa_lle(struct in6_ifaddr *, int); int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *, struct route *); /* nd6_nbr.c */ void nd6_na_input(struct mbuf *, int, int); void nd6_na_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, u_long, int, struct sockaddr *); void nd6_ns_input(struct mbuf *, int, int); void nd6_ns_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *); caddr_t nd6_ifptomac(struct ifnet *); void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); /* nd6_rtr.c */ void nd6_rs_input(struct mbuf *, int, int); void nd6_ra_input(struct mbuf *, int, int); void prelist_del(struct nd_prefix *); void defrouter_reset(void); void defrouter_select(void); void defrtrlist_del(struct nd_defrouter *); void prelist_remove(struct nd_prefix *); int nd6_prelist_add(struct nd_prefixctl *, struct nd_defrouter *, struct nd_prefix **); void pfxlist_onlink_check(void); struct nd_defrouter *defrouter_lookup(struct in6_addr *, struct ifnet *); struct nd_prefix *nd6_prefix_lookup(struct nd_prefixctl *); void rt6_flush(struct in6_addr *, struct ifnet *); int nd6_setdefaultiface(int); int in6_tmpifadd(const struct in6_ifaddr *, int, int); #endif /* _KERNEL */ #endif /* _NETINET6_ND6_H_ */ Index: projects/clang380-import/sys/netinet6/nd6_nbr.c =================================================================== --- projects/clang380-import/sys/netinet6/nd6_nbr.c (revision 293015) +++ projects/clang380-import/sys/netinet6/nd6_nbr.c (revision 293016) @@ -1,1599 +1,1611 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE 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. * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: nd6_nbr.c,v 1.86 2002/01/21 02:33:04 jinmei Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_mpath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RADIX_MPATH #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define SDL(s) ((struct sockaddr_dl *)s) struct dadq; static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *); static void nd6_dad_add(struct dadq *dp); static void nd6_dad_del(struct dadq *dp); static void nd6_dad_rele(struct dadq *); static void nd6_dad_starttimer(struct dadq *, int, int); static void nd6_dad_stoptimer(struct dadq *); static void nd6_dad_timer(struct dadq *); static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); static void nd6_dad_ns_output(struct dadq *); static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *); static void nd6_dad_na_input(struct ifaddr *); static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, const struct in6_addr *, u_long, int, struct sockaddr *, u_int); static void nd6_ns_output_fib(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *, u_int); static VNET_DEFINE(int, dad_enhanced) = 1; #define V_dad_enhanced VNET(dad_enhanced) SYSCTL_DECL(_net_inet6_ip6); SYSCTL_INT(_net_inet6_ip6, OID_AUTO, dad_enhanced, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dad_enhanced), 0, "Enable Enhanced DAD, which adds a random nonce to NS messages for DAD."); static VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to transmit DAD packet */ #define V_dad_maxtry VNET(dad_maxtry) /* * Input a Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; int rflag; union nd_opts ndopts; struct sockaddr_dl proxydl; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0; if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif) rflag = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (in6_setscope(&taddr6, ifp, NULL) != 0) goto bad; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be a solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && /* don't check ifindex portion */ daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else if (!V_nd6_onlink_ns_rfc4861) { struct sockaddr_in6 src_sa6; /* * According to recent IETF discussions, it is not a good idea * to accept a NS from an address which would not be deemed * to be a neighbor otherwise. This point is expected to be * clarified in future revisions of the specification. */ bzero(&src_sa6, sizeof(src_sa6)); src_sa6.sin6_family = AF_INET6; src_sa6.sin6_len = sizeof(src_sa6); src_sa6.sin6_addr = saddr6; if (nd6_is_addr_neighbor(&src_sa6, ifp) == 0) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is unicast/anycast MUST NOT add * NS IP dst is solicited-node multicast MUST add * * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ if (ifp->if_carp) ifa = (*carp_iamatch6_p)(ifp, &taddr6); else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ if (ifa == NULL) { struct route_in6 ro; int need_proxy; bzero(&ro, sizeof(ro)); ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro.ro_dst.sin6_family = AF_INET6; ro.ro_dst.sin6_addr = taddr6; /* Always use the default FIB. */ #ifdef RADIX_MPATH rtalloc_mpath_fib((struct route *)&ro, ntohl(taddr6.s6_addr32[3]), RT_DEFAULT_FIB); #else in6_rtalloc(&ro, RT_DEFAULT_FIB); #endif need_proxy = (ro.ro_rt && (ro.ro_rt->rt_flags & RTF_ANNOUNCE) != 0 && ro.ro_rt->rt_gateway->sa_family == AF_LINK); if (ro.ro_rt != NULL) { if (need_proxy) proxydl = *SDL(ro.ro_rt->rt_gateway); RTFREE(ro.ro_rt); } if (need_proxy) { /* * proxy NDP for single entry */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if (ifa) proxy = 1; } } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(ip6bufs, &taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicate address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { struct in6_addr in6_all; in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; nd6_na_output_fib(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output_fib(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag | ND_NA_FLAG_SOLICITED, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); freeit: if (ifa != NULL) ifa_free(ifa); m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(ip6bufs, &saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(ip6bufs, &daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(ip6bufs, &taddr6))); ICMP6STAT_INC(icp6s_badns); if (ifa != NULL) ifa_free(ifa); m_freem(m); } /* * Output a Neighbor Solicitation Message. Caller specifies: * - ICMP6 header source IP6 address * - ND6 header target IP6 address * - ND6 header source datalink address * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) * * ln - for source address determination * nonce - If non-NULL, NS is used for duplicate address detection and * the value (length is ND_OPT_NONCE_LEN) is used as a random nonce. */ static void nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6, const struct in6_addr *daddr6, const struct in6_addr *taddr6, uint8_t *nonce, u_int fibnum) { struct mbuf *m; struct m_tag *mtag; struct ip6_hdr *ip6; struct nd_neighbor_solicit *nd_ns; struct ip6_moptions im6o; int icmp6len; int maxlen; caddr_t mac; struct route_in6 ro; if (IN6_IS_ADDR_MULTICAST(taddr6)) return; /* estimate the size of message */ maxlen = sizeof(*ip6) + sizeof(*nd_ns); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; KASSERT(max_linkhdr + maxlen <= MCLBYTES, ( "%s: max_linkhdr + maxlen > MCLBYTES (%d + %d > %d)", __func__, max_linkhdr, maxlen, MCLBYTES)); if (max_linkhdr + maxlen > MHLEN) m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) return; M_SETFIB(m, fibnum); bzero(&ro, sizeof(ro)); if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_hlim = 255; im6o.im6o_multicast_loop = 0; } icmp6len = sizeof(*nd_ns); m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; m->m_data += max_linkhdr; /* or M_ALIGN() equivalent? */ /* fill neighbor solicitation packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; if (daddr6) ip6->ip6_dst = *daddr6; else { ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; ip6->ip6_dst.s6_addr16[1] = 0; ip6->ip6_dst.s6_addr32[1] = 0; ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; ip6->ip6_dst.s6_addr8[12] = 0xff; if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) goto bad; } if (nonce == NULL) { struct ifaddr *ifa = NULL; /* * RFC2461 7.2.2: * "If the source address of the packet prompting the * solicitation is the same as one of the addresses assigned * to the outgoing interface, that address SHOULD be placed * in the IP Source Address of the outgoing solicitation. * Otherwise, any one of the addresses assigned to the * interface should be used." * * We use the source address for the prompting packet * (saddr6), if saddr6 belongs to the outgoing interface. * Otherwise, we perform the source address selection as usual. */ if (saddr6 != NULL) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, saddr6); if (ifa != NULL) { /* ip6_src set already. */ ip6->ip6_src = *saddr6; ifa_free(ifa); } else { int error; struct sockaddr_in6 dst_sa; struct in6_addr src_in; struct ifnet *oifp; bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; oifp = ifp; error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &oifp, &src_in); if (error) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "%s: source can't be " "determined: dst=%s, error=%d\n", __func__, ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error)); goto bad; } ip6->ip6_src = src_in; } } else { /* * Source address for DAD packet must always be IPv6 * unspecified address. (0::0) * We actually don't have to 0-clear the address (we did it * above), but we do so here explicitly to make the intention * clearer. */ bzero(&ip6->ip6_src, sizeof(ip6->ip6_src)); } nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; nd_ns->nd_ns_code = 0; nd_ns->nd_ns_reserved = 0; nd_ns->nd_ns_target = *taddr6; in6_clearscope(&nd_ns->nd_ns_target); /* XXX */ /* * Add source link-layer address option. * * spec implementation * --- --- * DAD packet MUST NOT do not add the option * there's no link layer address: * impossible do not add the option * there's link layer address: * Multicast NS MUST add one add the option * Unicast NS SHOULD add one add the option */ if (nonce == NULL && (mac = nd6_ifptomac(ifp))) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); /* 8 byte alignments... */ optlen = (optlen + 7) & ~7; m->m_pkthdr.len += optlen; m->m_len += optlen; icmp6len += optlen; bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); } /* * Add a Nonce option (RFC 3971) to detect looped back NS messages. * This behavior is documented as Enhanced Duplicate Address * Detection in RFC 7527. * net.inet6.ip6.dad_enhanced=0 disables this. */ if (V_dad_enhanced != 0 && nonce != NULL) { int optlen = sizeof(struct nd_opt_hdr) + ND_OPT_NONCE_LEN; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); /* 8-byte alignment is required. */ optlen = (optlen + 7) & ~7; m->m_pkthdr.len += optlen; m->m_len += optlen; icmp6len += optlen; bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_NONCE; nd_opt->nd_opt_len = optlen >> 3; bcopy(nonce, (caddr_t)(nd_opt + 1), ND_OPT_NONCE_LEN); } ip6->ip6_plen = htons((u_short)icmp6len); nd_ns->nd_ns_cksum = 0; nd_ns->nd_ns_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); if (send_sendso_input_hook != NULL) { mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, sizeof(unsigned short), M_NOWAIT); if (mtag == NULL) goto bad; *(unsigned short *)(mtag + 1) = nd_ns->nd_ns_type; m_tag_prepend(m, mtag); } ip6_output(m, NULL, &ro, (nonce != NULL) ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]); /* We don't cache this route. */ RO_RTFREE(&ro); return; bad: if (ro.ro_rt) { RTFREE(ro.ro_rt); } m_freem(m); return; } #ifndef BURN_BRIDGES void nd6_ns_output(struct ifnet *ifp, const struct in6_addr *saddr6, const struct in6_addr *daddr6, const struct in6_addr *taddr6,uint8_t *nonce) { nd6_ns_output_fib(ifp, saddr6, daddr6, taddr6, nonce, RT_DEFAULT_FIB); } #endif /* * Neighbor advertisement input handling. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) * * the following items are not implemented yet: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void nd6_na_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_advert *nd_na; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; int flags; int is_router; int is_solicited; int is_override; char *lladdr = NULL; int lladdrlen = 0; int checklink = 0; struct ifaddr *ifa; struct llentry *ln = NULL; union nd_opts ndopts; struct mbuf *chain = NULL; struct sockaddr_in6 sin6; + u_char linkhdr[LLE_MAX_LINKHDR]; + size_t linkhdrsize; + int lladdr_off; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); if (nd_na == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif flags = nd_na->nd_na_flags_reserved; is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); memset(&sin6, 0, sizeof(sin6)); taddr6 = nd_na->nd_na_target; if (in6_setscope(&taddr6, ifp, NULL)) goto bad; /* XXX: impossible */ if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_ERR, "nd6_na_input: invalid target address %s\n", ip6_sprintf(ip6bufs, &taddr6))); goto bad; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { nd6log((LOG_ERR, "nd6_na_input: a solicited adv is multicasted\n")); goto bad; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_na_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } /* * This effectively disables the DAD check on a non-master CARP * address. */ if (ifp->if_carp) ifa = (*carp_iamatch6_p)(ifp, &taddr6); else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* * Target address matches one of my interface address. * * If my address is tentative, this means that there's somebody * already using the same address as mine. This indicates DAD failure. * This is defined in RFC 2462. * * Otherwise, process as defined in RFC 2461. */ if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { nd6_dad_na_input(ifa); ifa_free(ifa); goto freeit; } /* Just for safety, maybe unnecessary. */ if (ifa) { ifa_free(ifa); log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &taddr6)); goto freeit; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " "(if %d, NA packet %d)\n", ip6_sprintf(ip6bufs, &taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } /* * If no neighbor cache entry is found, NA SHOULD silently be * discarded. */ IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(&taddr6, LLE_EXCLUSIVE, ifp); IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) { goto freeit; } if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* * If the link-layer has address, and no lladdr option came, * discard the packet. */ if (ifp->if_addrlen && lladdr == NULL) { goto freeit; } /* * Record link-layer address, and update the state. */ - if (lltable_try_set_entry_addr(ifp, ln, lladdr) == 0) { + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + return; + + if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, + lladdr_off) == 0) { ln = NULL; goto freeit; } EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); if (is_solicited) nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE); else nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); if ((ln->ln_router = is_router) != 0) { /* * This means a router's state has changed from * non-reachable to probably reachable, and might * affect the status of associated prefixes.. */ checklink = 1; } } else { int llchange; /* * Check if the link-layer address has changed or not. */ if (lladdr == NULL) llchange = 0; else { if (ln->la_flags & LLE_VALID) { - if (bcmp(lladdr, &ln->ll_addr, ifp->if_addrlen)) + if (bcmp(lladdr, ln->ll_addr, ifp->if_addrlen)) llchange = 1; else llchange = 0; } else llchange = 1; } /* * This is VERY complex. Look at it with care. * * override solicit lladdr llchange action * (L: record lladdr) * * 0 0 n -- (2c) * 0 0 y n (2b) L * 0 0 y y (1) REACHABLE->STALE * 0 1 n -- (2c) *->REACHABLE * 0 1 y n (2b) L *->REACHABLE * 0 1 y y (1) REACHABLE->STALE * 1 0 n -- (2a) * 1 0 y n (2a) L * 1 0 y y (2a) L *->STALE * 1 1 n -- (2a) *->REACHABLE * 1 1 y n (2a) L *->REACHABLE * 1 1 y y (2a) L *->REACHABLE */ if (!is_override && (lladdr != NULL && llchange)) { /* (1) */ /* * If state is REACHABLE, make it STALE. * no other updates should be done. */ if (ln->ln_state == ND6_LLINFO_REACHABLE) nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr != NULL && !llchange)) /* (2b) */ || lladdr == NULL) { /* (2c) */ /* * Update link-local address, if any. */ if (lladdr != NULL) { - int ret; - ret = lltable_try_set_entry_addr(ifp, ln,lladdr); - if (ret == 0) { + linkhdrsize = sizeof(linkhdr); + if (lltable_calc_llheader(ifp, AF_INET6, lladdr, + linkhdr, &linkhdrsize, &lladdr_off) != 0) + goto freeit; + if (lltable_try_set_entry_addr(ifp, ln, linkhdr, + linkhdrsize, lladdr_off) == 0) { ln = NULL; goto freeit; } EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); } /* * If solicited, make the state REACHABLE. * If not solicited and the link-layer address was * changed, make it STALE. */ if (is_solicited) nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE); else { if (lladdr != NULL && llchange) nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); } } if (ln->ln_router && !is_router) { /* * The peer dropped the router flag. * Remove the sender from the Default Router List and * update the Destination Cache entries. */ struct nd_defrouter *dr; struct in6_addr *in6; in6 = &ln->r_l3addr.addr6; /* * Lock to protect the default router list. * XXX: this might be unnecessary, since this function * is only called under the network software interrupt * context. However, we keep it just for safety. */ dr = defrouter_lookup(in6, ln->lle_tbl->llt_ifp); if (dr) defrtrlist_del(dr); else if (ND_IFINFO(ln->lle_tbl->llt_ifp)->flags & ND6_IFF_ACCEPT_RTADV) { /* * Even if the neighbor is not in the default * router list, the neighbor may be used * as a next hop for some destinations * (e.g. redirect case). So we must * call rt6_flush explicitly. */ rt6_flush(&ip6->ip6_src, ifp); } } ln->ln_router = is_router; } /* XXX - QL * Does this matter? * rt->rt_flags &= ~RTF_REJECT; */ ln->la_asked = 0; if (ln->la_hold != NULL) nd6_grab_holdchain(ln, &chain, &sin6); freeit: if (ln != NULL) LLE_WUNLOCK(ln); if (chain != NULL) nd6_flush_holdchain(ifp, ifp, chain, &sin6); if (checklink) pfxlist_onlink_check(); m_freem(m); return; bad: if (ln != NULL) LLE_WUNLOCK(ln); ICMP6STAT_INC(icp6s_badna); m_freem(m); } /* * Neighbor advertisement output handling. * * Based on RFC 2461 * * the following items are not implemented yet: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) * * tlladdr - 1 if include target link-layer address * sdl0 - sockaddr_dl (= proxy NA) or NULL */ static void nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, const struct in6_addr *taddr6, u_long flags, int tlladdr, struct sockaddr *sdl0, u_int fibnum) { struct mbuf *m; struct m_tag *mtag; struct ifnet *oifp; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na; struct ip6_moptions im6o; struct in6_addr src, daddr6; struct sockaddr_in6 dst_sa; int icmp6len, maxlen, error; caddr_t mac = NULL; struct route_in6 ro; bzero(&ro, sizeof(ro)); daddr6 = *daddr6_0; /* make a local copy for modification */ /* estimate the size of message */ maxlen = sizeof(*ip6) + sizeof(*nd_na); maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; KASSERT(max_linkhdr + maxlen <= MCLBYTES, ( "%s: max_linkhdr + maxlen > MCLBYTES (%d + %d > %d)", __func__, max_linkhdr, maxlen, MCLBYTES)); if (max_linkhdr + maxlen > MHLEN) m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) return; M_SETFIB(m, fibnum); if (IN6_IS_ADDR_MULTICAST(&daddr6)) { m->m_flags |= M_MCAST; im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_hlim = 255; im6o.im6o_multicast_loop = 0; } icmp6len = sizeof(*nd_na); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; m->m_data += max_linkhdr; /* or M_ALIGN() equivalent? */ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { /* reply to DAD */ daddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL; daddr6.s6_addr16[1] = 0; daddr6.s6_addr32[1] = 0; daddr6.s6_addr32[2] = 0; daddr6.s6_addr32[3] = IPV6_ADDR_INT32_ONE; if (in6_setscope(&daddr6, ifp, NULL)) goto bad; flags &= ~ND_NA_FLAG_SOLICITED; } ip6->ip6_dst = daddr6; bzero(&dst_sa, sizeof(struct sockaddr_in6)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(struct sockaddr_in6); dst_sa.sin6_addr = daddr6; /* * Select a source whose scope is the same as that of the dest. */ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); oifp = ifp; error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &oifp, &src); if (error) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "nd6_na_output: source can't be " "determined: dst=%s, error=%d\n", ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error)); goto bad; } ip6->ip6_src = src; nd_na = (struct nd_neighbor_advert *)(ip6 + 1); nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_code = 0; nd_na->nd_na_target = *taddr6; in6_clearscope(&nd_na->nd_na_target); /* XXX */ /* * "tlladdr" indicates NS's condition for adding tlladdr or not. * see nd6_ns_input() for details. * Basically, if NS packet is sent to unicast/anycast addr, * target lladdr option SHOULD NOT be included. */ if (tlladdr) { /* * sdl0 != NULL indicates proxy NA. If we do proxy, use * lladdr in sdl0. If we are not proxying (sending NA for * my address) use lladdr configured for the interface. */ if (sdl0 == NULL) { if (ifp->if_carp) mac = (*carp_macmatch6_p)(ifp, m, taddr6); if (mac == NULL) mac = nd6_ifptomac(ifp); } else if (sdl0->sa_family == AF_LINK) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)sdl0; if (sdl->sdl_alen == ifp->if_addrlen) mac = LLADDR(sdl); } } if (tlladdr && mac) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); /* roundup to 8 bytes alignment! */ optlen = (optlen + 7) & ~7; m->m_pkthdr.len += optlen; m->m_len += optlen; icmp6len += optlen; bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); } else flags &= ~ND_NA_FLAG_OVERRIDE; ip6->ip6_plen = htons((u_short)icmp6len); nd_na->nd_na_flags_reserved = flags; nd_na->nd_na_cksum = 0; nd_na->nd_na_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); if (send_sendso_input_hook != NULL) { mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, sizeof(unsigned short), M_NOWAIT); if (mtag == NULL) goto bad; *(unsigned short *)(mtag + 1) = nd_na->nd_na_type; m_tag_prepend(m, mtag); } ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL); icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_ADVERT]); /* We don't cache this route. */ RO_RTFREE(&ro); return; bad: if (ro.ro_rt) { RTFREE(ro.ro_rt); } m_freem(m); return; } #ifndef BURN_BRIDGES void nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, const struct in6_addr *taddr6, u_long flags, int tlladdr, struct sockaddr *sdl0) { nd6_na_output_fib(ifp, daddr6_0, taddr6, flags, tlladdr, sdl0, RT_DEFAULT_FIB); } #endif caddr_t nd6_ifptomac(struct ifnet *ifp) { switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: case IFT_IEEE1394: case IFT_L2VLAN: case IFT_IEEE80211: case IFT_INFINIBAND: case IFT_BRIDGE: case IFT_ISO88025: return IF_LLADDR(ifp); default: return NULL; } } struct dadq { TAILQ_ENTRY(dadq) dad_list; struct ifaddr *dad_ifa; int dad_count; /* max NS to send */ int dad_ns_tcount; /* # of trials to send NS */ int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; int dad_ns_lcount; /* looped back NS */ int dad_loopbackprobe; /* probing state for loopback detection */ struct callout dad_timer_ch; struct vnet *dad_vnet; u_int dad_refcnt; #define ND_OPT_NONCE_LEN32 \ ((ND_OPT_NONCE_LEN + sizeof(uint32_t) - 1)/sizeof(uint32_t)) uint32_t dad_nonce[ND_OPT_NONCE_LEN32]; }; static VNET_DEFINE(TAILQ_HEAD(, dadq), dadq); static VNET_DEFINE(struct rwlock, dad_rwlock); #define V_dadq VNET(dadq) #define V_dad_rwlock VNET(dad_rwlock) #define DADQ_RLOCK() rw_rlock(&V_dad_rwlock) #define DADQ_RUNLOCK() rw_runlock(&V_dad_rwlock) #define DADQ_WLOCK() rw_wlock(&V_dad_rwlock) #define DADQ_WUNLOCK() rw_wunlock(&V_dad_rwlock) static void nd6_dad_add(struct dadq *dp) { DADQ_WLOCK(); TAILQ_INSERT_TAIL(&V_dadq, dp, dad_list); DADQ_WUNLOCK(); } static void nd6_dad_del(struct dadq *dp) { DADQ_WLOCK(); TAILQ_REMOVE(&V_dadq, dp, dad_list); DADQ_WUNLOCK(); nd6_dad_rele(dp); } static struct dadq * nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n) { struct dadq *dp; DADQ_RLOCK(); TAILQ_FOREACH(dp, &V_dadq, dad_list) { if (dp->dad_ifa != ifa) continue; /* * Skip if the nonce matches the received one. * +2 in the length is required because of type and * length fields are included in a header. */ if (n != NULL && n->nd_opt_nonce_len == (ND_OPT_NONCE_LEN + 2) / 8 && memcmp(&n->nd_opt_nonce[0], &dp->dad_nonce[0], ND_OPT_NONCE_LEN) == 0) { dp->dad_ns_lcount++; continue; } refcount_acquire(&dp->dad_refcnt); break; } DADQ_RUNLOCK(); return (dp); } static void nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns) { if (send_ns != 0) nd6_dad_ns_output(dp); callout_reset(&dp->dad_timer_ch, ticks, (void (*)(void *))nd6_dad_timer, (void *)dp); } static void nd6_dad_stoptimer(struct dadq *dp) { callout_drain(&dp->dad_timer_ch); } static void nd6_dad_rele(struct dadq *dp) { if (refcount_release(&dp->dad_refcnt)) { ifa_free(dp->dad_ifa); free(dp, M_IP6NDP); } } void nd6_dad_init(void) { rw_init(&V_dad_rwlock, "nd6 DAD queue"); TAILQ_INIT(&V_dadq); } /* * Start Duplicate Address Detection (DAD) for specified interface address. */ void nd6_dad_start(struct ifaddr *ifa, int delay) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; char ip6buf[INET6_ADDRSTRLEN]; int send_ns; /* * If we don't need DAD, don't do it. * There are several cases: * - DAD is disabled (ip6_dad_count == 0) * - the interface address is anycast */ if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { log(LOG_DEBUG, "nd6_dad_start: called with non-tentative address " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); return; } if (ia->ia6_flags & IN6_IFF_ANYCAST) { ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } if (!V_ip6_dad_count) { ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } if (ifa->ifa_ifp == NULL) panic("nd6_dad_start: ifa->ifa_ifp == NULL"); if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_NO_DAD) { ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } if (!(ifa->ifa_ifp->if_flags & IFF_UP) || !(ifa->ifa_ifp->if_drv_flags & IFF_DRV_RUNNING) || (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED)) { ia->ia6_flags |= IN6_IFF_TENTATIVE; return; } if ((dp = nd6_dad_find(ifa, NULL)) != NULL) { /* * DAD already in progress. Let the existing entry * to finish it. */ return; } dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT | M_ZERO); if (dp == NULL) { log(LOG_ERR, "nd6_dad_start: memory allocation failed for " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); return; } callout_init(&dp->dad_timer_ch, 0); #ifdef VIMAGE dp->dad_vnet = curvnet; #endif nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); /* * Send NS packet for DAD, ip6_dad_count times. * Note that we must delay the first transmission, if this is the * first packet to be sent from the interface after interface * (re)initialization. */ dp->dad_ifa = ifa; ifa_ref(dp->dad_ifa); dp->dad_count = V_ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0; dp->dad_ns_lcount = dp->dad_loopbackprobe = 0; refcount_init(&dp->dad_refcnt, 1); nd6_dad_add(dp); send_ns = 0; if (delay == 0) { send_ns = 1; delay = (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000; } nd6_dad_starttimer(dp, delay, send_ns); } /* * terminate DAD unconditionally. used for address removals. */ void nd6_dad_stop(struct ifaddr *ifa) { struct dadq *dp; dp = nd6_dad_find(ifa, NULL); if (!dp) { /* DAD wasn't started yet */ return; } nd6_dad_stoptimer(dp); /* * The DAD queue entry may have been removed by nd6_dad_timer() while * we were waiting for it to stop, so re-do the lookup. */ nd6_dad_rele(dp); if (nd6_dad_find(ifa, NULL) == NULL) return; nd6_dad_del(dp); nd6_dad_rele(dp); } static void nd6_dad_timer(struct dadq *dp) { CURVNET_SET(dp->dad_vnet); struct ifaddr *ifa = dp->dad_ifa; struct ifnet *ifp = dp->dad_ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; char ip6buf[INET6_ADDRSTRLEN]; /* Sanity check */ if (ia == NULL) { log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); goto err; } if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { /* Do not need DAD for ifdisabled interface. */ log(LOG_ERR, "nd6_dad_timer: cancel DAD on %s because of " "ND6_IFF_IFDISABLED.\n", ifp->if_xname); goto err; } if (ia->ia6_flags & IN6_IFF_DUPLICATED) { log(LOG_ERR, "nd6_dad_timer: called with duplicated address " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto err; } if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " "%s(%s)\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto err; } /* Stop DAD if the interface is down even after dad_maxtry attempts. */ if ((dp->dad_ns_tcount > V_dad_maxtry) && (((ifp->if_flags & IFF_UP) == 0) || ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0))) { nd6log((LOG_INFO, "%s: could not run DAD " "because the interface was down or not running.\n", if_name(ifa->ifa_ifp))); goto err; } /* Need more checks? */ if (dp->dad_ns_ocount < dp->dad_count) { /* * We have more NS to go. Send NS packet for DAD. */ nd6_dad_starttimer(dp, (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1); goto done; } else { /* * We have transmitted sufficient number of DAD packets. * See what we've got. */ if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0) /* We've seen NS or NA, means DAD has failed. */ nd6_dad_duplicated(ifa, dp); else if (V_dad_enhanced != 0 && dp->dad_ns_lcount > 0 && dp->dad_ns_lcount > dp->dad_loopbackprobe) { /* * Sec. 4.1 in RFC 7527 requires transmission of * additional probes until the loopback condition * becomes clear when a looped back probe is detected. */ log(LOG_ERR, "%s: a looped back NS message is " "detected during DAD for %s. " "Another DAD probes are being sent.\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, IFA_IN6(ifa))); dp->dad_loopbackprobe = dp->dad_ns_lcount; /* * Send an NS immediately and increase dad_count by * V_nd6_mmaxtries - 1. */ dp->dad_count = dp->dad_ns_ocount + V_nd6_mmaxtries - 1; nd6_dad_starttimer(dp, (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1); goto done; } else { /* * We are done with DAD. No NA came, no NS came. * No duplicate address found. Check IFDISABLED flag * again in case that it is changed between the * beginning of this function and here. */ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) ia->ia6_flags &= ~IN6_IFF_TENTATIVE; nd6log((LOG_DEBUG, "%s: DAD complete for %s - no duplicates found\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); if (dp->dad_ns_lcount > 0) log(LOG_ERR, "%s: DAD completed while " "a looped back NS message is detected " "during DAD for %s.\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, IFA_IN6(ifa))); } } err: nd6_dad_del(dp); done: CURVNET_RESTORE(); } static void nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp) { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct ifnet *ifp; char ip6buf[INET6_ADDRSTRLEN]; log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " "NS in/out/loopback=%d/%d/%d, NA in=%d\n", if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_ns_lcount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; ifp = ifa->ifa_ifp; log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)); log(LOG_ERR, "%s: manual intervention required\n", if_name(ifp)); /* * If the address is a link-local address formed from an interface * identifier based on the hardware address which is supposed to be * uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP * operation on the interface SHOULD be disabled. * [RFC 4862, Section 5.4.5] */ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) { struct in6_addr in6; /* * To avoid over-reaction, we only apply this logic when we are * very sure that hardware addresses are supposed to be unique. */ switch (ifp->if_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ATM: case IFT_IEEE1394: case IFT_IEEE80211: case IFT_INFINIBAND: in6 = ia->ia_addr.sin6_addr; if (in6_get_hw_ifid(ifp, &in6) == 0 && IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &in6)) { ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED; log(LOG_ERR, "%s: possible hardware address " "duplication detected, disable IPv6\n", if_name(ifp)); } break; } } } static void nd6_dad_ns_output(struct dadq *dp) { struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa; struct ifnet *ifp = dp->dad_ifa->ifa_ifp; int i; dp->dad_ns_tcount++; if ((ifp->if_flags & IFF_UP) == 0) { return; } if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { return; } dp->dad_ns_ocount++; if (V_dad_enhanced != 0) { for (i = 0; i < ND_OPT_NONCE_LEN32; i++) dp->dad_nonce[i] = arc4random(); /* * XXXHRS: Note that in the case that * DupAddrDetectTransmits > 1, multiple NS messages with * different nonces can be looped back in an unexpected * order. The current implementation recognizes only * the latest nonce on the sender side. Practically it * should work well in almost all cases. */ } nd6_ns_output(ifp, NULL, NULL, &ia->ia_addr.sin6_addr, (uint8_t *)&dp->dad_nonce[0]); } static void nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce) { struct in6_ifaddr *ia; struct ifnet *ifp; const struct in6_addr *taddr6; struct dadq *dp; if (ifa == NULL) panic("ifa == NULL in nd6_dad_ns_input"); ia = (struct in6_ifaddr *)ifa; ifp = ifa->ifa_ifp; taddr6 = &ia->ia_addr.sin6_addr; /* Ignore Nonce option when Enhanced DAD is disabled. */ if (V_dad_enhanced == 0) ndopt_nonce = NULL; dp = nd6_dad_find(ifa, ndopt_nonce); if (dp == NULL) return; dp->dad_ns_icount++; nd6_dad_rele(dp); } static void nd6_dad_na_input(struct ifaddr *ifa) { struct dadq *dp; if (ifa == NULL) panic("ifa == NULL in nd6_dad_na_input"); dp = nd6_dad_find(ifa, NULL); if (dp != NULL) { dp->dad_na_icount++; nd6_dad_rele(dp); } } Index: projects/clang380-import/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c =================================================================== --- projects/clang380-import/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c (revision 293015) +++ projects/clang380-import/sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c (revision 293016) @@ -1,1544 +1,1544 @@ /* * Copyright (c) 2004 Topspin Communications. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2004 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "ipoib.h" static int ipoib_resolvemulti(struct ifnet *, struct sockaddr **, struct sockaddr *); #include #include #include #include #include /* For ARPHRD_xxx */ #include #include #include MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("IP-over-InfiniBand net driver"); MODULE_LICENSE("Dual BSD/GPL"); int ipoib_sendq_size = IPOIB_TX_RING_SIZE; int ipoib_recvq_size = IPOIB_RX_RING_SIZE; module_param_named(send_queue_size, ipoib_sendq_size, int, 0444); MODULE_PARM_DESC(send_queue_size, "Number of descriptors in send queue"); module_param_named(recv_queue_size, ipoib_recvq_size, int, 0444); MODULE_PARM_DESC(recv_queue_size, "Number of descriptors in receive queue"); #ifdef CONFIG_INFINIBAND_IPOIB_DEBUG int ipoib_debug_level = 1; module_param_named(debug_level, ipoib_debug_level, int, 0644); MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0"); #endif struct ipoib_path_iter { struct ipoib_dev_priv *priv; struct ipoib_path path; }; static const u8 ipv4_bcast_addr[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; struct workqueue_struct *ipoib_workqueue; struct ib_sa_client ipoib_sa_client; static void ipoib_add_one(struct ib_device *device); static void ipoib_remove_one(struct ib_device *device); static void ipoib_start(struct ifnet *dev); static int ipoib_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro); static int ipoib_ioctl(struct ifnet *ifp, u_long command, caddr_t data); static void ipoib_input(struct ifnet *ifp, struct mbuf *m); #define IPOIB_MTAP(_ifp, _m) \ do { \ if (bpf_peers_present((_ifp)->if_bpf)) { \ M_ASSERTVALID(_m); \ ipoib_mtap_mb((_ifp), (_m)); \ } \ } while (0) /* * This is for clients that have an ipoib_header in the mbuf. */ static void ipoib_mtap_mb(struct ifnet *ifp, struct mbuf *mb) { struct ipoib_header *ih; struct ether_header eh; ih = mtod(mb, struct ipoib_header *); eh.ether_type = ih->proto; bcopy(ih->hwaddr, &eh.ether_dhost, ETHER_ADDR_LEN); bzero(&eh.ether_shost, ETHER_ADDR_LEN); mb->m_data += sizeof(struct ipoib_header); mb->m_len -= sizeof(struct ipoib_header); bpf_mtap2(ifp->if_bpf, &eh, sizeof(eh), mb); mb->m_data -= sizeof(struct ipoib_header); mb->m_len += sizeof(struct ipoib_header); } void ipoib_mtap_proto(struct ifnet *ifp, struct mbuf *mb, uint16_t proto) { struct ether_header eh; eh.ether_type = proto; bzero(&eh.ether_shost, ETHER_ADDR_LEN); bzero(&eh.ether_dhost, ETHER_ADDR_LEN); bpf_mtap2(ifp->if_bpf, &eh, sizeof(eh), mb); } static struct ib_client ipoib_client = { .name = "ipoib", .add = ipoib_add_one, .remove = ipoib_remove_one }; int ipoib_open(struct ipoib_dev_priv *priv) { struct ifnet *dev = priv->dev; ipoib_dbg(priv, "bringing up interface\n"); set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); if (ipoib_pkey_dev_delay_open(priv)) return 0; if (ipoib_ib_dev_open(priv)) goto err_disable; if (ipoib_ib_dev_up(priv)) goto err_stop; if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { struct ipoib_dev_priv *cpriv; /* Bring up any child interfaces too */ mutex_lock(&priv->vlan_mutex); list_for_each_entry(cpriv, &priv->child_intfs, list) if ((cpriv->dev->if_drv_flags & IFF_DRV_RUNNING) == 0) ipoib_open(cpriv); mutex_unlock(&priv->vlan_mutex); } dev->if_drv_flags |= IFF_DRV_RUNNING; dev->if_drv_flags &= ~IFF_DRV_OACTIVE; return 0; err_stop: ipoib_ib_dev_stop(priv, 1); err_disable: clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); return -EINVAL; } static void ipoib_init(void *arg) { struct ifnet *dev; struct ipoib_dev_priv *priv; priv = arg; dev = priv->dev; if ((dev->if_drv_flags & IFF_DRV_RUNNING) == 0) ipoib_open(priv); queue_work(ipoib_workqueue, &priv->flush_light); } static int ipoib_stop(struct ipoib_dev_priv *priv) { struct ifnet *dev = priv->dev; ipoib_dbg(priv, "stopping interface\n"); clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); dev->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); ipoib_ib_dev_down(priv, 0); ipoib_ib_dev_stop(priv, 0); if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { struct ipoib_dev_priv *cpriv; /* Bring down any child interfaces too */ mutex_lock(&priv->vlan_mutex); list_for_each_entry(cpriv, &priv->child_intfs, list) if ((cpriv->dev->if_drv_flags & IFF_DRV_RUNNING) != 0) ipoib_stop(cpriv); mutex_unlock(&priv->vlan_mutex); } return 0; } int ipoib_change_mtu(struct ipoib_dev_priv *priv, int new_mtu) { struct ifnet *dev = priv->dev; /* dev->if_mtu > 2K ==> connected mode */ if (ipoib_cm_admin_enabled(priv)) { if (new_mtu > IPOIB_CM_MTU(ipoib_cm_max_mtu(priv))) return -EINVAL; if (new_mtu > priv->mcast_mtu) ipoib_warn(priv, "mtu > %d will cause multicast packet drops.\n", priv->mcast_mtu); dev->if_mtu = new_mtu; return 0; } if (new_mtu > IPOIB_UD_MTU(priv->max_ib_mtu)) return -EINVAL; priv->admin_mtu = new_mtu; dev->if_mtu = min(priv->mcast_mtu, priv->admin_mtu); queue_work(ipoib_workqueue, &priv->flush_light); return 0; } static int ipoib_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ipoib_dev_priv *priv = ifp->if_softc; struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) error = -ipoib_open(priv); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) ipoib_stop(priv); break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifp->if_drv_flags & IFF_DRV_RUNNING) queue_work(ipoib_workqueue, &priv->restart_task); break; case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(ifp, ifa); break; #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IF_LLADDR(ifp), (caddr_t) sa->sa_data, INFINIBAND_ALEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ error = -ipoib_change_mtu(priv, ifr->ifr_mtu); break; default: error = EINVAL; break; } return (error); } static struct ipoib_path * __path_find(struct ipoib_dev_priv *priv, void *gid) { struct rb_node *n = priv->path_tree.rb_node; struct ipoib_path *path; int ret; while (n) { path = rb_entry(n, struct ipoib_path, rb_node); ret = memcmp(gid, path->pathrec.dgid.raw, sizeof (union ib_gid)); if (ret < 0) n = n->rb_left; else if (ret > 0) n = n->rb_right; else return path; } return NULL; } static int __path_add(struct ipoib_dev_priv *priv, struct ipoib_path *path) { struct rb_node **n = &priv->path_tree.rb_node; struct rb_node *pn = NULL; struct ipoib_path *tpath; int ret; while (*n) { pn = *n; tpath = rb_entry(pn, struct ipoib_path, rb_node); ret = memcmp(path->pathrec.dgid.raw, tpath->pathrec.dgid.raw, sizeof (union ib_gid)); if (ret < 0) n = &pn->rb_left; else if (ret > 0) n = &pn->rb_right; else return -EEXIST; } rb_link_node(&path->rb_node, pn, n); rb_insert_color(&path->rb_node, &priv->path_tree); list_add_tail(&path->list, &priv->path_list); return 0; } void ipoib_path_free(struct ipoib_dev_priv *priv, struct ipoib_path *path) { _IF_DRAIN(&path->queue); if (path->ah) ipoib_put_ah(path->ah); if (ipoib_cm_get(path)) ipoib_cm_destroy_tx(ipoib_cm_get(path)); kfree(path); } #ifdef CONFIG_INFINIBAND_IPOIB_DEBUG struct ipoib_path_iter * ipoib_path_iter_init(struct ipoib_dev_priv *priv) { struct ipoib_path_iter *iter; iter = kmalloc(sizeof *iter, GFP_KERNEL); if (!iter) return NULL; iter->priv = priv; memset(iter->path.pathrec.dgid.raw, 0, 16); if (ipoib_path_iter_next(iter)) { kfree(iter); return NULL; } return iter; } int ipoib_path_iter_next(struct ipoib_path_iter *iter) { struct ipoib_dev_priv *priv = iter->priv; struct rb_node *n; struct ipoib_path *path; int ret = 1; spin_lock_irq(&priv->lock); n = rb_first(&priv->path_tree); while (n) { path = rb_entry(n, struct ipoib_path, rb_node); if (memcmp(iter->path.pathrec.dgid.raw, path->pathrec.dgid.raw, sizeof (union ib_gid)) < 0) { iter->path = *path; ret = 0; break; } n = rb_next(n); } spin_unlock_irq(&priv->lock); return ret; } void ipoib_path_iter_read(struct ipoib_path_iter *iter, struct ipoib_path *path) { *path = iter->path; } #endif /* CONFIG_INFINIBAND_IPOIB_DEBUG */ void ipoib_mark_paths_invalid(struct ipoib_dev_priv *priv) { struct ipoib_path *path, *tp; spin_lock_irq(&priv->lock); list_for_each_entry_safe(path, tp, &priv->path_list, list) { ipoib_dbg(priv, "mark path LID 0x%04x GID %16D invalid\n", be16_to_cpu(path->pathrec.dlid), path->pathrec.dgid.raw, ":"); path->valid = 0; } spin_unlock_irq(&priv->lock); } void ipoib_flush_paths(struct ipoib_dev_priv *priv) { struct ipoib_path *path, *tp; LIST_HEAD(remove_list); unsigned long flags; spin_lock_irqsave(&priv->lock, flags); list_splice_init(&priv->path_list, &remove_list); list_for_each_entry(path, &remove_list, list) rb_erase(&path->rb_node, &priv->path_tree); list_for_each_entry_safe(path, tp, &remove_list, list) { if (path->query) ib_sa_cancel_query(path->query_id, path->query); spin_unlock_irqrestore(&priv->lock, flags); wait_for_completion(&path->done); ipoib_path_free(priv, path); spin_lock_irqsave(&priv->lock, flags); } spin_unlock_irqrestore(&priv->lock, flags); } static void path_rec_completion(int status, struct ib_sa_path_rec *pathrec, void *path_ptr) { struct ipoib_path *path = path_ptr; struct ipoib_dev_priv *priv = path->priv; struct ifnet *dev = priv->dev; struct ipoib_ah *ah = NULL; struct ipoib_ah *old_ah = NULL; struct ifqueue mbqueue; struct mbuf *mb; unsigned long flags; if (!status) ipoib_dbg(priv, "PathRec LID 0x%04x for GID %16D\n", be16_to_cpu(pathrec->dlid), pathrec->dgid.raw, ":"); else ipoib_dbg(priv, "PathRec status %d for GID %16D\n", status, path->pathrec.dgid.raw, ":"); bzero(&mbqueue, sizeof(mbqueue)); if (!status) { struct ib_ah_attr av; if (!ib_init_ah_from_path(priv->ca, priv->port, pathrec, &av)) ah = ipoib_create_ah(priv, priv->pd, &av); } spin_lock_irqsave(&priv->lock, flags); if (ah) { path->pathrec = *pathrec; old_ah = path->ah; path->ah = ah; ipoib_dbg(priv, "created address handle %p for LID 0x%04x, SL %d\n", ah, be16_to_cpu(pathrec->dlid), pathrec->sl); for (;;) { _IF_DEQUEUE(&path->queue, mb); if (mb == NULL) break; _IF_ENQUEUE(&mbqueue, mb); } #ifdef CONFIG_INFINIBAND_IPOIB_CM if (ipoib_cm_enabled(priv, path->hwaddr) && !ipoib_cm_get(path)) ipoib_cm_set(path, ipoib_cm_create_tx(priv, path)); #endif path->valid = 1; } path->query = NULL; complete(&path->done); spin_unlock_irqrestore(&priv->lock, flags); if (old_ah) ipoib_put_ah(old_ah); for (;;) { _IF_DEQUEUE(&mbqueue, mb); if (mb == NULL) break; mb->m_pkthdr.rcvif = dev; if (dev->if_transmit(dev, mb)) ipoib_warn(priv, "dev_queue_xmit failed " "to requeue packet\n"); } } static struct ipoib_path * path_rec_create(struct ipoib_dev_priv *priv, uint8_t *hwaddr) { struct ipoib_path *path; if (!priv->broadcast) return NULL; path = kzalloc(sizeof *path, GFP_ATOMIC); if (!path) return NULL; path->priv = priv; bzero(&path->queue, sizeof(path->queue)); #ifdef CONFIG_INFINIBAND_IPOIB_CM memcpy(&path->hwaddr, hwaddr, INFINIBAND_ALEN); #endif memcpy(path->pathrec.dgid.raw, &hwaddr[4], sizeof (union ib_gid)); path->pathrec.sgid = priv->local_gid; path->pathrec.pkey = cpu_to_be16(priv->pkey); path->pathrec.numb_path = 1; path->pathrec.traffic_class = priv->broadcast->mcmember.traffic_class; return path; } static int path_rec_start(struct ipoib_dev_priv *priv, struct ipoib_path *path) { struct ifnet *dev = priv->dev; ib_sa_comp_mask comp_mask = IB_SA_PATH_REC_MTU_SELECTOR | IB_SA_PATH_REC_MTU; struct ib_sa_path_rec p_rec; p_rec = path->pathrec; p_rec.mtu_selector = IB_SA_GT; switch (roundup_pow_of_two(dev->if_mtu + IPOIB_ENCAP_LEN)) { case 512: p_rec.mtu = IB_MTU_256; break; case 1024: p_rec.mtu = IB_MTU_512; break; case 2048: p_rec.mtu = IB_MTU_1024; break; case 4096: p_rec.mtu = IB_MTU_2048; break; default: /* Wildcard everything */ comp_mask = 0; p_rec.mtu = 0; p_rec.mtu_selector = 0; } ipoib_dbg(priv, "Start path record lookup for %16D MTU > %d\n", p_rec.dgid.raw, ":", comp_mask ? ib_mtu_enum_to_int(p_rec.mtu) : 0); init_completion(&path->done); path->query_id = ib_sa_path_rec_get(&ipoib_sa_client, priv->ca, priv->port, &p_rec, comp_mask | IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID | IB_SA_PATH_REC_NUMB_PATH | IB_SA_PATH_REC_TRAFFIC_CLASS | IB_SA_PATH_REC_PKEY, 1000, GFP_ATOMIC, path_rec_completion, path, &path->query); if (path->query_id < 0) { ipoib_warn(priv, "ib_sa_path_rec_get failed: %d\n", path->query_id); path->query = NULL; complete(&path->done); return path->query_id; } return 0; } static void ipoib_unicast_send(struct mbuf *mb, struct ipoib_dev_priv *priv, struct ipoib_header *eh) { struct ipoib_path *path; path = __path_find(priv, eh->hwaddr + 4); if (!path || !path->valid) { int new_path = 0; if (!path) { path = path_rec_create(priv, eh->hwaddr); new_path = 1; } if (path) { _IF_ENQUEUE(&path->queue, mb); if (!path->query && path_rec_start(priv, path)) { spin_unlock_irqrestore(&priv->lock, flags); if (new_path) ipoib_path_free(priv, path); return; } else __path_add(priv, path); } else { if_inc_counter(priv->dev, IFCOUNTER_OERRORS, 1); m_freem(mb); } return; } if (ipoib_cm_get(path) && ipoib_cm_up(path)) { ipoib_cm_send(priv, mb, ipoib_cm_get(path)); } else if (path->ah) { ipoib_send(priv, mb, path->ah, IPOIB_QPN(eh->hwaddr)); } else if ((path->query || !path_rec_start(priv, path)) && path->queue.ifq_len < IPOIB_MAX_PATH_REC_QUEUE) { _IF_ENQUEUE(&path->queue, mb); } else { if_inc_counter(priv->dev, IFCOUNTER_OERRORS, 1); m_freem(mb); } } static int ipoib_send_one(struct ipoib_dev_priv *priv, struct mbuf *mb) { struct ipoib_header *eh; eh = mtod(mb, struct ipoib_header *); if (IPOIB_IS_MULTICAST(eh->hwaddr)) { /* Add in the P_Key for multicast*/ eh->hwaddr[8] = (priv->pkey >> 8) & 0xff; eh->hwaddr[9] = priv->pkey & 0xff; ipoib_mcast_send(priv, eh->hwaddr + 4, mb); } else ipoib_unicast_send(mb, priv, eh); return 0; } static void _ipoib_start(struct ifnet *dev, struct ipoib_dev_priv *priv) { struct mbuf *mb; if ((dev->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; spin_lock(&priv->lock); while (!IFQ_DRV_IS_EMPTY(&dev->if_snd) && (dev->if_drv_flags & IFF_DRV_OACTIVE) == 0) { IFQ_DRV_DEQUEUE(&dev->if_snd, mb); if (mb == NULL) break; IPOIB_MTAP(dev, mb); ipoib_send_one(priv, mb); } spin_unlock(&priv->lock); } static void ipoib_start(struct ifnet *dev) { _ipoib_start(dev, dev->if_softc); } static void ipoib_vlan_start(struct ifnet *dev) { struct ipoib_dev_priv *priv; struct mbuf *mb; priv = VLAN_COOKIE(dev); if (priv != NULL) return _ipoib_start(dev, priv); while (!IFQ_DRV_IS_EMPTY(&dev->if_snd)) { IFQ_DRV_DEQUEUE(&dev->if_snd, mb); if (mb == NULL) break; m_freem(mb); if_inc_counter(dev, IFCOUNTER_OERRORS, 1); } } int ipoib_dev_init(struct ipoib_dev_priv *priv, struct ib_device *ca, int port) { /* Allocate RX/TX "rings" to hold queued mbs */ priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring, GFP_KERNEL); if (!priv->rx_ring) { printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n", ca->name, ipoib_recvq_size); goto out; } priv->tx_ring = kzalloc(ipoib_sendq_size * sizeof *priv->tx_ring, GFP_KERNEL); if (!priv->tx_ring) { printk(KERN_WARNING "%s: failed to allocate TX ring (%d entries)\n", ca->name, ipoib_sendq_size); goto out_rx_ring_cleanup; } memset(priv->tx_ring, 0, ipoib_sendq_size * sizeof *priv->tx_ring); /* priv->tx_head, tx_tail & tx_outstanding are already 0 */ if (ipoib_ib_dev_init(priv, ca, port)) goto out_tx_ring_cleanup; return 0; out_tx_ring_cleanup: kfree(priv->tx_ring); out_rx_ring_cleanup: kfree(priv->rx_ring); out: return -ENOMEM; } static void ipoib_detach(struct ipoib_dev_priv *priv) { struct ifnet *dev; dev = priv->dev; if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { bpfdetach(dev); if_detach(dev); if_free(dev); } else VLAN_SETCOOKIE(priv->dev, NULL); free(priv, M_TEMP); } void ipoib_dev_cleanup(struct ipoib_dev_priv *priv) { struct ipoib_dev_priv *cpriv, *tcpriv; /* Delete any child interfaces first */ list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) { ipoib_dev_cleanup(cpriv); ipoib_detach(cpriv); } ipoib_ib_dev_cleanup(priv); kfree(priv->rx_ring); kfree(priv->tx_ring); priv->rx_ring = NULL; priv->tx_ring = NULL; } static volatile int ipoib_unit; static struct ipoib_dev_priv * ipoib_priv_alloc(void) { struct ipoib_dev_priv *priv; priv = malloc(sizeof(struct ipoib_dev_priv), M_TEMP, M_ZERO|M_WAITOK); spin_lock_init(&priv->lock); spin_lock_init(&priv->drain_lock); mutex_init(&priv->vlan_mutex); INIT_LIST_HEAD(&priv->path_list); INIT_LIST_HEAD(&priv->child_intfs); INIT_LIST_HEAD(&priv->dead_ahs); INIT_LIST_HEAD(&priv->multicast_list); INIT_DELAYED_WORK(&priv->pkey_poll_task, ipoib_pkey_poll); INIT_DELAYED_WORK(&priv->mcast_task, ipoib_mcast_join_task); INIT_WORK(&priv->carrier_on_task, ipoib_mcast_carrier_on_task); INIT_WORK(&priv->flush_light, ipoib_ib_dev_flush_light); INIT_WORK(&priv->flush_normal, ipoib_ib_dev_flush_normal); INIT_WORK(&priv->flush_heavy, ipoib_ib_dev_flush_heavy); INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task); INIT_DELAYED_WORK(&priv->ah_reap_task, ipoib_reap_ah); memcpy(priv->broadcastaddr, ipv4_bcast_addr, INFINIBAND_ALEN); return (priv); } struct ipoib_dev_priv * ipoib_intf_alloc(const char *name) { struct ipoib_dev_priv *priv; struct sockaddr_dl *sdl; struct ifnet *dev; priv = ipoib_priv_alloc(); dev = priv->dev = if_alloc(IFT_INFINIBAND); if (!dev) { free(priv, M_TEMP); return NULL; } dev->if_softc = priv; if_initname(dev, name, atomic_fetchadd_int(&ipoib_unit, 1)); dev->if_flags = IFF_BROADCAST | IFF_MULTICAST; dev->if_addrlen = INFINIBAND_ALEN; dev->if_hdrlen = IPOIB_HEADER_LEN; if_attach(dev); dev->if_init = ipoib_init; dev->if_ioctl = ipoib_ioctl; dev->if_start = ipoib_start; dev->if_output = ipoib_output; dev->if_input = ipoib_input; dev->if_resolvemulti = ipoib_resolvemulti; dev->if_baudrate = IF_Gbps(10); dev->if_broadcastaddr = priv->broadcastaddr; dev->if_snd.ifq_maxlen = ipoib_sendq_size * 2; sdl = (struct sockaddr_dl *)dev->if_addr->ifa_addr; sdl->sdl_type = IFT_INFINIBAND; sdl->sdl_alen = dev->if_addrlen; priv->dev = dev; if_link_state_change(dev, LINK_STATE_DOWN); bpfattach(dev, DLT_EN10MB, ETHER_HDR_LEN); return dev->if_softc; } int ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca) { struct ib_device_attr *device_attr; int result = -ENOMEM; device_attr = kmalloc(sizeof *device_attr, GFP_KERNEL); if (!device_attr) { printk(KERN_WARNING "%s: allocation of %zu bytes failed\n", hca->name, sizeof *device_attr); return result; } result = ib_query_device(hca, device_attr); if (result) { printk(KERN_WARNING "%s: ib_query_device failed (ret = %d)\n", hca->name, result); kfree(device_attr); return result; } priv->hca_caps = device_attr->device_cap_flags; kfree(device_attr); priv->dev->if_hwassist = 0; priv->dev->if_capabilities = 0; #ifndef CONFIG_INFINIBAND_IPOIB_CM if (priv->hca_caps & IB_DEVICE_UD_IP_CSUM) { set_bit(IPOIB_FLAG_CSUM, &priv->flags); priv->dev->if_hwassist = CSUM_IP | CSUM_TCP | CSUM_UDP; priv->dev->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; } #if 0 if (priv->dev->features & NETIF_F_SG && priv->hca_caps & IB_DEVICE_UD_TSO) { priv->dev->if_capabilities |= IFCAP_TSO4; priv->dev->if_hwassist |= CSUM_TSO; } #endif #endif priv->dev->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_LINKSTATE; priv->dev->if_capenable = priv->dev->if_capabilities; return 0; } static struct ifnet * ipoib_add_port(const char *format, struct ib_device *hca, u8 port) { struct ipoib_dev_priv *priv; struct ib_port_attr attr; int result = -ENOMEM; priv = ipoib_intf_alloc(format); if (!priv) goto alloc_mem_failed; if (!ib_query_port(hca, port, &attr)) priv->max_ib_mtu = ib_mtu_enum_to_int(attr.max_mtu); else { printk(KERN_WARNING "%s: ib_query_port %d failed\n", hca->name, port); goto device_init_failed; } /* MTU will be reset when mcast join happens */ priv->dev->if_mtu = IPOIB_UD_MTU(priv->max_ib_mtu); priv->mcast_mtu = priv->admin_mtu = priv->dev->if_mtu; result = ib_query_pkey(hca, port, 0, &priv->pkey); if (result) { printk(KERN_WARNING "%s: ib_query_pkey port %d failed (ret = %d)\n", hca->name, port, result); goto device_init_failed; } if (ipoib_set_dev_features(priv, hca)) goto device_init_failed; /* * Set the full membership bit, so that we join the right * broadcast group, etc. */ priv->pkey |= 0x8000; priv->broadcastaddr[8] = priv->pkey >> 8; priv->broadcastaddr[9] = priv->pkey & 0xff; result = ib_query_gid(hca, port, 0, &priv->local_gid); if (result) { printk(KERN_WARNING "%s: ib_query_gid port %d failed (ret = %d)\n", hca->name, port, result); goto device_init_failed; } memcpy(IF_LLADDR(priv->dev) + 4, priv->local_gid.raw, sizeof (union ib_gid)); result = ipoib_dev_init(priv, hca, port); if (result < 0) { printk(KERN_WARNING "%s: failed to initialize port %d (ret = %d)\n", hca->name, port, result); goto device_init_failed; } if (ipoib_cm_admin_enabled(priv)) priv->dev->if_mtu = IPOIB_CM_MTU(ipoib_cm_max_mtu(priv)); INIT_IB_EVENT_HANDLER(&priv->event_handler, priv->ca, ipoib_event); result = ib_register_event_handler(&priv->event_handler); if (result < 0) { printk(KERN_WARNING "%s: ib_register_event_handler failed for " "port %d (ret = %d)\n", hca->name, port, result); goto event_failed; } if_printf(priv->dev, "Attached to %s port %d\n", hca->name, port); return priv->dev; event_failed: ipoib_dev_cleanup(priv); device_init_failed: ipoib_detach(priv); alloc_mem_failed: return ERR_PTR(result); } static void ipoib_add_one(struct ib_device *device) { struct list_head *dev_list; struct ifnet *dev; struct ipoib_dev_priv *priv; int s, e, p; if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) return; dev_list = kmalloc(sizeof *dev_list, GFP_KERNEL); if (!dev_list) return; INIT_LIST_HEAD(dev_list); if (device->node_type == RDMA_NODE_IB_SWITCH) { s = 0; e = 0; } else { s = 1; e = device->phys_port_cnt; } for (p = s; p <= e; ++p) { if (rdma_port_get_link_layer(device, p) != IB_LINK_LAYER_INFINIBAND) continue; dev = ipoib_add_port("ib", device, p); if (!IS_ERR(dev)) { priv = dev->if_softc; list_add_tail(&priv->list, dev_list); } } ib_set_client_data(device, &ipoib_client, dev_list); } static void ipoib_remove_one(struct ib_device *device) { struct ipoib_dev_priv *priv, *tmp; struct list_head *dev_list; if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) return; dev_list = ib_get_client_data(device, &ipoib_client); list_for_each_entry_safe(priv, tmp, dev_list, list) { if (rdma_port_get_link_layer(device, priv->port) != IB_LINK_LAYER_INFINIBAND) continue; ipoib_stop(priv); ib_unregister_event_handler(&priv->event_handler); /* dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP); */ flush_workqueue(ipoib_workqueue); ipoib_dev_cleanup(priv); ipoib_detach(priv); } kfree(dev_list); } static void ipoib_config_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) { struct ipoib_dev_priv *parent; struct ipoib_dev_priv *priv; struct ifnet *dev; uint16_t pkey; int error; if (ifp->if_type != IFT_INFINIBAND) return; dev = VLAN_DEVAT(ifp, vtag); if (dev == NULL) return; priv = NULL; error = 0; parent = ifp->if_softc; /* We only support 15 bits of pkey. */ if (vtag & 0x8000) return; pkey = vtag | 0x8000; /* Set full membership bit. */ if (pkey == parent->pkey) return; /* Check for dups */ mutex_lock(&parent->vlan_mutex); list_for_each_entry(priv, &parent->child_intfs, list) { if (priv->pkey == pkey) { priv = NULL; error = EBUSY; goto out; } } priv = ipoib_priv_alloc(); priv->dev = dev; priv->max_ib_mtu = parent->max_ib_mtu; priv->mcast_mtu = priv->admin_mtu = parent->dev->if_mtu; set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags); error = ipoib_set_dev_features(priv, parent->ca); if (error) goto out; priv->pkey = pkey; priv->broadcastaddr[8] = pkey >> 8; priv->broadcastaddr[9] = pkey & 0xff; dev->if_broadcastaddr = priv->broadcastaddr; error = ipoib_dev_init(priv, parent->ca, parent->port); if (error) goto out; priv->parent = parent->dev; list_add_tail(&priv->list, &parent->child_intfs); VLAN_SETCOOKIE(dev, priv); dev->if_start = ipoib_vlan_start; dev->if_drv_flags &= ~IFF_DRV_RUNNING; dev->if_hdrlen = IPOIB_HEADER_LEN; if (ifp->if_drv_flags & IFF_DRV_RUNNING) ipoib_open(priv); mutex_unlock(&parent->vlan_mutex); return; out: mutex_unlock(&parent->vlan_mutex); if (priv) free(priv, M_TEMP); if (error) ipoib_warn(parent, "failed to initialize subinterface: device %s, port %d vtag 0x%X", parent->ca->name, parent->port, vtag); return; } static void ipoib_unconfig_vlan(void *arg, struct ifnet *ifp, u_int16_t vtag) { struct ipoib_dev_priv *parent; struct ipoib_dev_priv *priv; struct ifnet *dev; uint16_t pkey; if (ifp->if_type != IFT_INFINIBAND) return; dev = VLAN_DEVAT(ifp, vtag); if (dev) VLAN_SETCOOKIE(dev, NULL); pkey = vtag | 0x8000; parent = ifp->if_softc; mutex_lock(&parent->vlan_mutex); list_for_each_entry(priv, &parent->child_intfs, list) { if (priv->pkey == pkey) { ipoib_dev_cleanup(priv); list_del(&priv->list); break; } } mutex_unlock(&parent->vlan_mutex); } eventhandler_tag ipoib_vlan_attach; eventhandler_tag ipoib_vlan_detach; static int __init ipoib_init_module(void) { int ret; ipoib_recvq_size = roundup_pow_of_two(ipoib_recvq_size); ipoib_recvq_size = min(ipoib_recvq_size, IPOIB_MAX_QUEUE_SIZE); ipoib_recvq_size = max(ipoib_recvq_size, IPOIB_MIN_QUEUE_SIZE); ipoib_sendq_size = roundup_pow_of_two(ipoib_sendq_size); ipoib_sendq_size = min(ipoib_sendq_size, IPOIB_MAX_QUEUE_SIZE); ipoib_sendq_size = max(ipoib_sendq_size, max(2 * MAX_SEND_CQE, IPOIB_MIN_QUEUE_SIZE)); #ifdef CONFIG_INFINIBAND_IPOIB_CM ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP); #endif ipoib_vlan_attach = EVENTHANDLER_REGISTER(vlan_config, ipoib_config_vlan, NULL, EVENTHANDLER_PRI_FIRST); ipoib_vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, ipoib_unconfig_vlan, NULL, EVENTHANDLER_PRI_FIRST); /* * We create our own workqueue mainly because we want to be * able to flush it when devices are being removed. We can't * use schedule_work()/flush_scheduled_work() because both * unregister_netdev() and linkwatch_event take the rtnl lock, * so flush_scheduled_work() can deadlock during device * removal. */ ipoib_workqueue = create_singlethread_workqueue("ipoib"); if (!ipoib_workqueue) { ret = -ENOMEM; goto err_fs; } ib_sa_register_client(&ipoib_sa_client); ret = ib_register_client(&ipoib_client); if (ret) goto err_sa; return 0; err_sa: ib_sa_unregister_client(&ipoib_sa_client); destroy_workqueue(ipoib_workqueue); err_fs: return ret; } static void __exit ipoib_cleanup_module(void) { EVENTHANDLER_DEREGISTER(vlan_config, ipoib_vlan_attach); EVENTHANDLER_DEREGISTER(vlan_unconfig, ipoib_vlan_detach); ib_unregister_client(&ipoib_client); ib_sa_unregister_client(&ipoib_sa_client); destroy_workqueue(ipoib_workqueue); } /* * Infiniband output routine. */ static int ipoib_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro) { u_char edst[INFINIBAND_ALEN]; +#if defined(INET) || defined(INET6) struct llentry *lle = NULL; +#endif struct rtentry *rt0 = NULL; struct ipoib_header *eh; int error = 0, is_gw = 0; short type; if (ro != NULL) { - if (!(m->m_flags & (M_BCAST | M_MCAST))) - lle = ro->ro_lle; rt0 = ro->ro_rt; if (rt0 != NULL && (rt0->rt_flags & RTF_GATEWAY) != 0) is_gw = 1; } #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) goto bad; #endif M_PROFILE(m); if (ifp->if_flags & IFF_MONITOR) { error = ENETDOWN; goto bad; } if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) { error = ENETDOWN; goto bad; } switch (dst->sa_family) { #ifdef INET case AF_INET: if (lle != NULL && (lle->la_flags & LLE_VALID)) - memcpy(edst, &lle->ll_addr.mac8, sizeof(edst)); + memcpy(edst, lle->ll_addr, sizeof(edst)); else if (m->m_flags & M_MCAST) ip_ib_mc_map(((struct sockaddr_in *)dst)->sin_addr.s_addr, ifp->if_broadcastaddr, edst); else error = arpresolve(ifp, is_gw, m, dst, edst, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IP); break; case AF_ARP: { struct arphdr *ah; ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_INFINIBAND); switch(ntohs(ah->ar_op)) { case ARPOP_REVREQUEST: case ARPOP_REVREPLY: type = htons(ETHERTYPE_REVARP); break; case ARPOP_REQUEST: case ARPOP_REPLY: default: type = htons(ETHERTYPE_ARP); break; } if (m->m_flags & M_BCAST) bcopy(ifp->if_broadcastaddr, edst, INFINIBAND_ALEN); else bcopy(ar_tha(ah), edst, INFINIBAND_ALEN); } break; #endif #ifdef INET6 case AF_INET6: if (lle != NULL && (lle->la_flags & LLE_VALID)) - memcpy(edst, &lle->ll_addr.mac8, sizeof(edst)); + memcpy(edst, lle->ll_addr, sizeof(edst)); else if (m->m_flags & M_MCAST) ipv6_ib_mc_map(&((struct sockaddr_in6 *)dst)->sin6_addr, ifp->if_broadcastaddr, edst); else error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL); if (error) return error; type = htons(ETHERTYPE_IPV6); break; #endif default: if_printf(ifp, "can't handle af%d\n", dst->sa_family); error = EAFNOSUPPORT; goto bad; } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, IPOIB_HEADER_LEN, M_NOWAIT); if (m == NULL) { error = ENOBUFS; goto bad; } eh = mtod(m, struct ipoib_header *); (void)memcpy(&eh->proto, &type, sizeof(eh->proto)); (void)memcpy(&eh->hwaddr, edst, sizeof (edst)); /* * Queue message on interface, update output statistics if * successful, and start output if interface not yet active. */ return ((ifp->if_transmit)(ifp, m)); bad: if (m != NULL) m_freem(m); return (error); } /* * Upper layer processing for a received Infiniband packet. */ void ipoib_demux(struct ifnet *ifp, struct mbuf *m, u_short proto) { int isr; #ifdef MAC /* * Tag the mbuf with an appropriate MAC label before any other * consumers can get to it. */ mac_ifnet_create_mbuf(ifp, m); #endif /* Allow monitor mode to claim this frame, after stats are updated. */ if (ifp->if_flags & IFF_MONITOR) { if_printf(ifp, "discard frame at IFF_MONITOR\n"); m_freem(m); return; } /* * Dispatch frame to upper layer. */ switch (proto) { #ifdef INET case ETHERTYPE_IP: isr = NETISR_IP; break; case ETHERTYPE_ARP: if (ifp->if_flags & IFF_NOARP) { /* Discard packet if ARP is disabled on interface */ m_freem(m); return; } isr = NETISR_ARP; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: isr = NETISR_IPV6; break; #endif default: goto discard; } netisr_dispatch(isr, m); return; discard: m_freem(m); } /* * Process a received Infiniband packet. */ static void ipoib_input(struct ifnet *ifp, struct mbuf *m) { struct ipoib_header *eh; if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } CURVNET_SET_QUIET(ifp->if_vnet); /* Let BPF have it before we strip the header. */ IPOIB_MTAP(ifp, m); eh = mtod(m, struct ipoib_header *); /* * Reset layer specific mbuf flags to avoid confusing upper layers. * Strip off Infiniband header. */ m->m_flags &= ~M_VLANTAG; m_clrprotoflags(m); m_adj(m, IPOIB_HEADER_LEN); if (IPOIB_IS_MULTICAST(eh->hwaddr)) { if (memcmp(eh->hwaddr, ifp->if_broadcastaddr, ifp->if_addrlen) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); } ipoib_demux(ifp, m, ntohs(eh->proto)); CURVNET_RESTORE(); } static int ipoib_resolvemulti(struct ifnet *ifp, struct sockaddr **llsa, struct sockaddr *sa) { struct sockaddr_dl *sdl; #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if (!IPOIB_IS_MULTICAST(e_addr)) return EADDRNOTAVAIL; *llsa = 0; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_INFINIBAND); sdl->sdl_alen = INFINIBAND_ALEN; e_addr = LLADDR(sdl); ip_ib_mc_map(sin->sin_addr.s_addr, ifp->if_broadcastaddr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; /* * An IP6 address of 0 means listen to all * of the multicast address used for IP6. * This has no meaning in ipoib. */ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) return EADDRNOTAVAIL; if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; sdl = link_init_sdl(ifp, *llsa, IFT_INFINIBAND); sdl->sdl_alen = INFINIBAND_ALEN; e_addr = LLADDR(sdl); ipv6_ib_mc_map(&sin6->sin6_addr, ifp->if_broadcastaddr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: return EAFNOSUPPORT; } } module_init(ipoib_init_module); module_exit(ipoib_cleanup_module); static int ipoib_evhand(module_t mod, int event, void *arg) { return (0); } static moduledata_t ipoib_mod = { .name = "ipoib", .evhand = ipoib_evhand, }; DECLARE_MODULE(ipoib, ipoib_mod, SI_SUB_SMP, SI_ORDER_ANY); MODULE_DEPEND(ipoib, ibcore, 1, 1, 1); MODULE_DEPEND(ipoib, linuxkpi, 1, 1, 1); Index: projects/clang380-import/sys/opencrypto/skipjack.h =================================================================== --- projects/clang380-import/sys/opencrypto/skipjack.h (revision 293015) +++ projects/clang380-import/sys/opencrypto/skipjack.h (revision 293016) @@ -1,19 +1,24 @@ /* $FreeBSD$ */ /* $OpenBSD: skipjack.h,v 1.3 2002/03/14 01:26:51 millert Exp $ */ /*- * Further optimized test implementation of SKIPJACK algorithm * Mark Tillotson , 25 June 98 * Optimizations suit RISC (lots of registers) machine best. * * based on unoptimized implementation of * Panu Rissanen 960624 * * SKIPJACK and KEA Algorithm Specifications * Version 2.0 * 29 May 1998 */ +#ifndef _SKIPJACK_H_ +#define _SKIPJACK_H_ + extern void skipjack_forwards(u_int8_t *plain, u_int8_t *cipher, u_int8_t **key); extern void skipjack_backwards(u_int8_t *cipher, u_int8_t *plain, u_int8_t **key); extern void subkey_table_gen(u_int8_t *key, u_int8_t **key_tables); + +#endif Index: projects/clang380-import/sys/opencrypto/xform.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform.c (revision 293015) +++ projects/clang380-import/sys/opencrypto/xform.c (revision 293016) @@ -1,985 +1,113 @@ /* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ /*- * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr), * Niels Provos (provos@physnet.uni-hamburg.de) and * Damien Miller (djm@mindrot.org). * * This code was written by John Ioannidis for BSD/OS in Athens, Greece, * in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. * * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis * and Niels Provos. * * Additional features in 1999 by Angelos D. Keromytis. * * AES XTS implementation in 2008 by Damien Miller * * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, * Angelos D. Keromytis and Niels Provos. * * Copyright (C) 2001, Angelos D. Keromytis. * * Copyright (C) 2008, Damien Miller * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or * modification of this software. * You may use this code under the GNU public license if you so wish. Please * contribute changes back to the authors under this freer than GPL license * so that we may further the use of strong encryption without limitations to * all. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -static int null_setkey(u_int8_t **, u_int8_t *, int); -static int des1_setkey(u_int8_t **, u_int8_t *, int); -static int des3_setkey(u_int8_t **, u_int8_t *, int); -static int blf_setkey(u_int8_t **, u_int8_t *, int); -static int cast5_setkey(u_int8_t **, u_int8_t *, int); -static int skipjack_setkey(u_int8_t **, u_int8_t *, int); -static int rijndael128_setkey(u_int8_t **, u_int8_t *, int); -static int aes_icm_setkey(u_int8_t **, u_int8_t *, int); -static int aes_xts_setkey(u_int8_t **, u_int8_t *, int); -static int cml_setkey(u_int8_t **, u_int8_t *, int); - -static void null_encrypt(caddr_t, u_int8_t *); -static void des1_encrypt(caddr_t, u_int8_t *); -static void des3_encrypt(caddr_t, u_int8_t *); -static void blf_encrypt(caddr_t, u_int8_t *); -static void cast5_encrypt(caddr_t, u_int8_t *); -static void skipjack_encrypt(caddr_t, u_int8_t *); -static void rijndael128_encrypt(caddr_t, u_int8_t *); -static void aes_xts_encrypt(caddr_t, u_int8_t *); -static void cml_encrypt(caddr_t, u_int8_t *); - -static void null_decrypt(caddr_t, u_int8_t *); -static void des1_decrypt(caddr_t, u_int8_t *); -static void des3_decrypt(caddr_t, u_int8_t *); -static void blf_decrypt(caddr_t, u_int8_t *); -static void cast5_decrypt(caddr_t, u_int8_t *); -static void skipjack_decrypt(caddr_t, u_int8_t *); -static void rijndael128_decrypt(caddr_t, u_int8_t *); -static void aes_xts_decrypt(caddr_t, u_int8_t *); -static void cml_decrypt(caddr_t, u_int8_t *); - -static void aes_icm_crypt(caddr_t, u_int8_t *); - -static void null_zerokey(u_int8_t **); -static void des1_zerokey(u_int8_t **); -static void des3_zerokey(u_int8_t **); -static void blf_zerokey(u_int8_t **); -static void cast5_zerokey(u_int8_t **); -static void skipjack_zerokey(u_int8_t **); -static void rijndael128_zerokey(u_int8_t **); -static void aes_icm_zerokey(u_int8_t **); -static void aes_xts_zerokey(u_int8_t **); -static void cml_zerokey(u_int8_t **); - -static void aes_icm_reinit(caddr_t, u_int8_t *); -static void aes_xts_reinit(caddr_t, u_int8_t *); -static void aes_gcm_reinit(caddr_t, u_int8_t *); - -static void null_init(void *); -static void null_reinit(void *ctx, const u_int8_t *buf, u_int16_t len); -static int null_update(void *, const u_int8_t *, u_int16_t); -static void null_final(u_int8_t *, void *); -static int MD5Update_int(void *, const u_int8_t *, u_int16_t); -static void SHA1Init_int(void *); -static int SHA1Update_int(void *, const u_int8_t *, u_int16_t); -static void SHA1Final_int(u_int8_t *, void *); -static int RMD160Update_int(void *, const u_int8_t *, u_int16_t); -static int SHA256Update_int(void *, const u_int8_t *, u_int16_t); -static int SHA384Update_int(void *, const u_int8_t *, u_int16_t); -static int SHA512Update_int(void *, const u_int8_t *, u_int16_t); - -static u_int32_t deflate_compress(u_int8_t *, u_int32_t, u_int8_t **); -static u_int32_t deflate_decompress(u_int8_t *, u_int32_t, u_int8_t **); - -#define AESICM_BLOCKSIZE AES_BLOCK_LEN - -struct aes_icm_ctx { - u_int32_t ac_ek[4*(RIJNDAEL_MAXNR + 1)]; - /* ac_block is initalized to IV */ - u_int8_t ac_block[AESICM_BLOCKSIZE]; - int ac_nr; -}; - MALLOC_DEFINE(M_XDATA, "xform", "xform data buffers"); /* Encryption instances */ -struct enc_xform enc_xform_null = { - CRYPTO_NULL_CBC, "NULL", - /* NB: blocksize of 4 is to generate a properly aligned ESP header */ - NULL_BLOCK_LEN, 0, NULL_MIN_KEY, NULL_MAX_KEY, - null_encrypt, - null_decrypt, - null_setkey, - null_zerokey, - NULL, -}; - -struct enc_xform enc_xform_des = { - CRYPTO_DES_CBC, "DES", - DES_BLOCK_LEN, DES_BLOCK_LEN, DES_MIN_KEY, DES_MAX_KEY, - des1_encrypt, - des1_decrypt, - des1_setkey, - des1_zerokey, - NULL, -}; - -struct enc_xform enc_xform_3des = { - CRYPTO_3DES_CBC, "3DES", - DES3_BLOCK_LEN, DES3_BLOCK_LEN, TRIPLE_DES_MIN_KEY, - TRIPLE_DES_MAX_KEY, - des3_encrypt, - des3_decrypt, - des3_setkey, - des3_zerokey, - NULL, -}; - -struct enc_xform enc_xform_blf = { - CRYPTO_BLF_CBC, "Blowfish", - BLOWFISH_BLOCK_LEN, BLOWFISH_BLOCK_LEN, BLOWFISH_MIN_KEY, - BLOWFISH_MAX_KEY, - blf_encrypt, - blf_decrypt, - blf_setkey, - blf_zerokey, - NULL, -}; - -struct enc_xform enc_xform_cast5 = { - CRYPTO_CAST_CBC, "CAST-128", - CAST128_BLOCK_LEN, CAST128_BLOCK_LEN, CAST_MIN_KEY, CAST_MAX_KEY, - cast5_encrypt, - cast5_decrypt, - cast5_setkey, - cast5_zerokey, - NULL, -}; - -struct enc_xform enc_xform_skipjack = { - CRYPTO_SKIPJACK_CBC, "Skipjack", - SKIPJACK_BLOCK_LEN, SKIPJACK_BLOCK_LEN, SKIPJACK_MIN_KEY, - SKIPJACK_MAX_KEY, - skipjack_encrypt, - skipjack_decrypt, skipjack_setkey, - skipjack_zerokey, - NULL, -}; - -struct enc_xform enc_xform_rijndael128 = { - CRYPTO_RIJNDAEL128_CBC, "Rijndael-128/AES", - RIJNDAEL128_BLOCK_LEN, RIJNDAEL128_BLOCK_LEN, RIJNDAEL_MIN_KEY, - RIJNDAEL_MAX_KEY, - rijndael128_encrypt, - rijndael128_decrypt, - rijndael128_setkey, - rijndael128_zerokey, - NULL, -}; - -struct enc_xform enc_xform_aes_icm = { - CRYPTO_AES_ICM, "AES-ICM", - AES_BLOCK_LEN, AES_BLOCK_LEN, AES_MIN_KEY, AES_MAX_KEY, - aes_icm_crypt, - aes_icm_crypt, - aes_icm_setkey, - rijndael128_zerokey, - aes_icm_reinit, -}; - -struct enc_xform enc_xform_aes_nist_gcm = { - CRYPTO_AES_NIST_GCM_16, "AES-GCM", - AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY, - aes_icm_crypt, - aes_icm_crypt, - aes_icm_setkey, - aes_icm_zerokey, - aes_gcm_reinit, -}; - -struct enc_xform enc_xform_aes_nist_gmac = { - CRYPTO_AES_NIST_GMAC, "AES-GMAC", - AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY, - NULL, - NULL, - NULL, - NULL, - NULL, -}; - -struct enc_xform enc_xform_aes_xts = { - CRYPTO_AES_XTS, "AES-XTS", - AES_BLOCK_LEN, AES_XTS_IV_LEN, AES_XTS_MIN_KEY, AES_XTS_MAX_KEY, - aes_xts_encrypt, - aes_xts_decrypt, - aes_xts_setkey, - aes_xts_zerokey, - aes_xts_reinit -}; - struct enc_xform enc_xform_arc4 = { CRYPTO_ARC4, "ARC4", ARC4_BLOCK_LEN, ARC4_IV_LEN, ARC4_MIN_KEY, ARC4_MAX_KEY, NULL, NULL, NULL, NULL, NULL, }; -struct enc_xform enc_xform_camellia = { - CRYPTO_CAMELLIA_CBC, "Camellia", - CAMELLIA_BLOCK_LEN, CAMELLIA_BLOCK_LEN, CAMELLIA_MIN_KEY, - CAMELLIA_MAX_KEY, - cml_encrypt, - cml_decrypt, - cml_setkey, - cml_zerokey, - NULL, -}; -/* Authentication instances */ -struct auth_hash auth_hash_null = { /* NB: context isn't used */ - CRYPTO_NULL_HMAC, "NULL-HMAC", - NULL_HMAC_KEY_LEN, NULL_HASH_LEN, sizeof(int), NULL_HMAC_BLOCK_LEN, - null_init, null_reinit, null_reinit, null_update, null_final -}; +/* Include the encryption algorithms */ +#include "xform_null.c" +#include "xform_des1.c" +#include "xform_des3.c" +#include "xform_blf.c" +#include "xform_cast5.c" +#include "xform_skipjack.c" +#include "xform_rijndael.c" +#include "xform_aes_icm.c" +#include "xform_aes_xts.c" +#include "xform_cml.c" -struct auth_hash auth_hash_hmac_md5 = { - CRYPTO_MD5_HMAC, "HMAC-MD5", - MD5_HMAC_KEY_LEN, MD5_HASH_LEN, sizeof(MD5_CTX), MD5_HMAC_BLOCK_LEN, - (void (*) (void *)) MD5Init, NULL, NULL, MD5Update_int, - (void (*) (u_int8_t *, void *)) MD5Final -}; +/* Include the authentication and hashing algorithms */ +#include "xform_gmac.c" +#include "xform_md5.c" +#include "xform_rmd160.c" +#include "xform_sha1.c" +#include "xform_sha2.c" -struct auth_hash auth_hash_hmac_sha1 = { - CRYPTO_SHA1_HMAC, "HMAC-SHA1", - SHA1_HMAC_KEY_LEN, SHA1_HASH_LEN, sizeof(SHA1_CTX), SHA1_HMAC_BLOCK_LEN, - SHA1Init_int, NULL, NULL, SHA1Update_int, SHA1Final_int -}; +/* Include the compression algorithms */ +#include "xform_deflate.c" -struct auth_hash auth_hash_hmac_ripemd_160 = { - CRYPTO_RIPEMD160_HMAC, "HMAC-RIPEMD-160", - RIPEMD160_HMAC_KEY_LEN, RIPEMD160_HASH_LEN, sizeof(RMD160_CTX), - RIPEMD160_HMAC_BLOCK_LEN, - (void (*)(void *)) RMD160Init, NULL, NULL, RMD160Update_int, - (void (*)(u_int8_t *, void *)) RMD160Final -}; - -struct auth_hash auth_hash_key_md5 = { - CRYPTO_MD5_KPDK, "Keyed MD5", - NULL_HMAC_KEY_LEN, MD5_KPDK_HASH_LEN, sizeof(MD5_CTX), 0, - (void (*)(void *)) MD5Init, NULL, NULL, MD5Update_int, - (void (*)(u_int8_t *, void *)) MD5Final -}; - -struct auth_hash auth_hash_key_sha1 = { - CRYPTO_SHA1_KPDK, "Keyed SHA1", - NULL_HMAC_KEY_LEN, SHA1_KPDK_HASH_LEN, sizeof(SHA1_CTX), 0, - SHA1Init_int, NULL, NULL, SHA1Update_int, SHA1Final_int -}; - -struct auth_hash auth_hash_hmac_sha2_256 = { - CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256", - SHA2_256_HMAC_KEY_LEN, SHA2_256_HASH_LEN, sizeof(SHA256_CTX), - SHA2_256_HMAC_BLOCK_LEN, - (void (*)(void *)) SHA256_Init, NULL, NULL, SHA256Update_int, - (void (*)(u_int8_t *, void *)) SHA256_Final -}; - -struct auth_hash auth_hash_hmac_sha2_384 = { - CRYPTO_SHA2_384_HMAC, "HMAC-SHA2-384", - SHA2_384_HMAC_KEY_LEN, SHA2_384_HASH_LEN, sizeof(SHA384_CTX), - SHA2_384_HMAC_BLOCK_LEN, - (void (*)(void *)) SHA384_Init, NULL, NULL, SHA384Update_int, - (void (*)(u_int8_t *, void *)) SHA384_Final -}; - -struct auth_hash auth_hash_hmac_sha2_512 = { - CRYPTO_SHA2_512_HMAC, "HMAC-SHA2-512", - SHA2_512_HMAC_KEY_LEN, SHA2_512_HASH_LEN, sizeof(SHA512_CTX), - SHA2_512_HMAC_BLOCK_LEN, - (void (*)(void *)) SHA512_Init, NULL, NULL, SHA512Update_int, - (void (*)(u_int8_t *, void *)) SHA512_Final -}; - -struct auth_hash auth_hash_nist_gmac_aes_128 = { - CRYPTO_AES_128_NIST_GMAC, "GMAC-AES-128", - AES_128_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), - GMAC_BLOCK_LEN, - (void (*)(void *)) AES_GMAC_Init, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, - (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, - (void (*)(u_int8_t *, void *)) AES_GMAC_Final -}; - -struct auth_hash auth_hash_nist_gmac_aes_192 = { - CRYPTO_AES_192_NIST_GMAC, "GMAC-AES-192", - AES_192_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), - GMAC_BLOCK_LEN, - (void (*)(void *)) AES_GMAC_Init, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, - (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, - (void (*)(u_int8_t *, void *)) AES_GMAC_Final -}; - -struct auth_hash auth_hash_nist_gmac_aes_256 = { - CRYPTO_AES_256_NIST_GMAC, "GMAC-AES-256", - AES_256_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), - GMAC_BLOCK_LEN, - (void (*)(void *)) AES_GMAC_Init, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, - (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, - (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, - (void (*)(u_int8_t *, void *)) AES_GMAC_Final -}; - -/* Compression instance */ -struct comp_algo comp_algo_deflate = { - CRYPTO_DEFLATE_COMP, "Deflate", - 90, deflate_compress, - deflate_decompress -}; - -/* - * Encryption wrapper routines. - */ -static void -null_encrypt(caddr_t key, u_int8_t *blk) -{ -} -static void -null_decrypt(caddr_t key, u_int8_t *blk) -{ -} -static int -null_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - *sched = NULL; - return 0; -} -static void -null_zerokey(u_int8_t **sched) -{ - *sched = NULL; -} - -static void -des1_encrypt(caddr_t key, u_int8_t *blk) -{ - des_cblock *cb = (des_cblock *) blk; - des_key_schedule *p = (des_key_schedule *) key; - - des_ecb_encrypt(cb, cb, p[0], DES_ENCRYPT); -} - -static void -des1_decrypt(caddr_t key, u_int8_t *blk) -{ - des_cblock *cb = (des_cblock *) blk; - des_key_schedule *p = (des_key_schedule *) key; - - des_ecb_encrypt(cb, cb, p[0], DES_DECRYPT); -} - -static int -des1_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - des_key_schedule *p; - int err; - - p = malloc(sizeof (des_key_schedule), - M_CRYPTO_DATA, M_NOWAIT|M_ZERO); - if (p != NULL) { - des_set_key((des_cblock *) key, p[0]); - err = 0; - } else - err = ENOMEM; - *sched = (u_int8_t *) p; - return err; -} - -static void -des1_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof (des_key_schedule)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -des3_encrypt(caddr_t key, u_int8_t *blk) -{ - des_cblock *cb = (des_cblock *) blk; - des_key_schedule *p = (des_key_schedule *) key; - - des_ecb3_encrypt(cb, cb, p[0], p[1], p[2], DES_ENCRYPT); -} - -static void -des3_decrypt(caddr_t key, u_int8_t *blk) -{ - des_cblock *cb = (des_cblock *) blk; - des_key_schedule *p = (des_key_schedule *) key; - - des_ecb3_encrypt(cb, cb, p[0], p[1], p[2], DES_DECRYPT); -} - -static int -des3_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - des_key_schedule *p; - int err; - - p = malloc(3*sizeof (des_key_schedule), - M_CRYPTO_DATA, M_NOWAIT|M_ZERO); - if (p != NULL) { - des_set_key((des_cblock *)(key + 0), p[0]); - des_set_key((des_cblock *)(key + 8), p[1]); - des_set_key((des_cblock *)(key + 16), p[2]); - err = 0; - } else - err = ENOMEM; - *sched = (u_int8_t *) p; - return err; -} - -static void -des3_zerokey(u_int8_t **sched) -{ - bzero(*sched, 3*sizeof (des_key_schedule)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -blf_encrypt(caddr_t key, u_int8_t *blk) -{ - BF_LONG t[2]; - - memcpy(t, blk, sizeof (t)); - t[0] = ntohl(t[0]); - t[1] = ntohl(t[1]); - /* NB: BF_encrypt expects the block in host order! */ - BF_encrypt(t, (BF_KEY *) key); - t[0] = htonl(t[0]); - t[1] = htonl(t[1]); - memcpy(blk, t, sizeof (t)); -} - -static void -blf_decrypt(caddr_t key, u_int8_t *blk) -{ - BF_LONG t[2]; - - memcpy(t, blk, sizeof (t)); - t[0] = ntohl(t[0]); - t[1] = ntohl(t[1]); - /* NB: BF_decrypt expects the block in host order! */ - BF_decrypt(t, (BF_KEY *) key); - t[0] = htonl(t[0]); - t[1] = htonl(t[1]); - memcpy(blk, t, sizeof (t)); -} - -static int -blf_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - int err; - - *sched = malloc(sizeof(BF_KEY), - M_CRYPTO_DATA, M_NOWAIT|M_ZERO); - if (*sched != NULL) { - BF_set_key((BF_KEY *) *sched, len, key); - err = 0; - } else - err = ENOMEM; - return err; -} - -static void -blf_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(BF_KEY)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -cast5_encrypt(caddr_t key, u_int8_t *blk) -{ - cast_encrypt((cast_key *) key, blk, blk); -} - -static void -cast5_decrypt(caddr_t key, u_int8_t *blk) -{ - cast_decrypt((cast_key *) key, blk, blk); -} - -static int -cast5_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - int err; - - *sched = malloc(sizeof(cast_key), M_CRYPTO_DATA, M_NOWAIT|M_ZERO); - if (*sched != NULL) { - cast_setkey((cast_key *)*sched, key, len); - err = 0; - } else - err = ENOMEM; - return err; -} - -static void -cast5_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(cast_key)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -skipjack_encrypt(caddr_t key, u_int8_t *blk) -{ - skipjack_forwards(blk, blk, (u_int8_t **) key); -} - -static void -skipjack_decrypt(caddr_t key, u_int8_t *blk) -{ - skipjack_backwards(blk, blk, (u_int8_t **) key); -} - -static int -skipjack_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - int err; - - /* NB: allocate all the memory that's needed at once */ - *sched = malloc(10 * (sizeof(u_int8_t *) + 0x100), - M_CRYPTO_DATA, M_NOWAIT|M_ZERO); - if (*sched != NULL) { - u_int8_t** key_tables = (u_int8_t**) *sched; - u_int8_t* table = (u_int8_t*) &key_tables[10]; - int k; - - for (k = 0; k < 10; k++) { - key_tables[k] = table; - table += 0x100; - } - subkey_table_gen(key, (u_int8_t **) *sched); - err = 0; - } else - err = ENOMEM; - return err; -} - -static void -skipjack_zerokey(u_int8_t **sched) -{ - bzero(*sched, 10 * (sizeof(u_int8_t *) + 0x100)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -rijndael128_encrypt(caddr_t key, u_int8_t *blk) -{ - rijndael_encrypt((rijndael_ctx *) key, (u_char *) blk, (u_char *) blk); -} - -static void -rijndael128_decrypt(caddr_t key, u_int8_t *blk) -{ - rijndael_decrypt(((rijndael_ctx *) key), (u_char *) blk, - (u_char *) blk); -} - -static int -rijndael128_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - int err; - - if (len != 16 && len != 24 && len != 32) - return (EINVAL); - *sched = malloc(sizeof(rijndael_ctx), M_CRYPTO_DATA, - M_NOWAIT|M_ZERO); - if (*sched != NULL) { - rijndael_set_key((rijndael_ctx *) *sched, (u_char *) key, - len * 8); - err = 0; - } else - err = ENOMEM; - return err; -} - -static void -rijndael128_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(rijndael_ctx)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -void -aes_icm_reinit(caddr_t key, u_int8_t *iv) -{ - struct aes_icm_ctx *ctx; - - ctx = (struct aes_icm_ctx *)key; - bcopy(iv, ctx->ac_block, AESICM_BLOCKSIZE); -} - -void -aes_gcm_reinit(caddr_t key, u_int8_t *iv) -{ - struct aes_icm_ctx *ctx; - - aes_icm_reinit(key, iv); - - ctx = (struct aes_icm_ctx *)key; - /* GCM starts with 2 as counter 1 is used for final xor of tag. */ - bzero(&ctx->ac_block[AESICM_BLOCKSIZE - 4], 4); - ctx->ac_block[AESICM_BLOCKSIZE - 1] = 2; -} - -void -aes_icm_crypt(caddr_t key, u_int8_t *data) -{ - struct aes_icm_ctx *ctx; - u_int8_t keystream[AESICM_BLOCKSIZE]; - int i; - - ctx = (struct aes_icm_ctx *)key; - rijndaelEncrypt(ctx->ac_ek, ctx->ac_nr, ctx->ac_block, keystream); - for (i = 0; i < AESICM_BLOCKSIZE; i++) - data[i] ^= keystream[i]; - explicit_bzero(keystream, sizeof(keystream)); - - /* increment counter */ - for (i = AESICM_BLOCKSIZE - 1; - i >= 0; i--) - if (++ctx->ac_block[i]) /* continue on overflow */ - break; -} - -int -aes_icm_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - struct aes_icm_ctx *ctx; - - if (len != 16 && len != 24 && len != 32) - return EINVAL; - - *sched = malloc(sizeof(struct aes_icm_ctx), M_CRYPTO_DATA, - M_NOWAIT | M_ZERO); - if (*sched == NULL) - return ENOMEM; - - ctx = (struct aes_icm_ctx *)*sched; - ctx->ac_nr = rijndaelKeySetupEnc(ctx->ac_ek, (u_char *)key, len * 8); - return 0; -} - -void -aes_icm_zerokey(u_int8_t **sched) -{ - - bzero(*sched, sizeof(struct aes_icm_ctx)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -#define AES_XTS_BLOCKSIZE 16 -#define AES_XTS_IVSIZE 8 -#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ - -struct aes_xts_ctx { - rijndael_ctx key1; - rijndael_ctx key2; - u_int8_t tweak[AES_XTS_BLOCKSIZE]; -}; - -void -aes_xts_reinit(caddr_t key, u_int8_t *iv) -{ - struct aes_xts_ctx *ctx = (struct aes_xts_ctx *)key; - u_int64_t blocknum; - u_int i; - - /* - * Prepare tweak as E_k2(IV). IV is specified as LE representation - * of a 64-bit block number which we allow to be passed in directly. - */ - bcopy(iv, &blocknum, AES_XTS_IVSIZE); - for (i = 0; i < AES_XTS_IVSIZE; i++) { - ctx->tweak[i] = blocknum & 0xff; - blocknum >>= 8; - } - /* Last 64 bits of IV are always zero */ - bzero(ctx->tweak + AES_XTS_IVSIZE, AES_XTS_IVSIZE); - - rijndael_encrypt(&ctx->key2, ctx->tweak, ctx->tweak); -} - -static void -aes_xts_crypt(struct aes_xts_ctx *ctx, u_int8_t *data, u_int do_encrypt) -{ - u_int8_t block[AES_XTS_BLOCKSIZE]; - u_int i, carry_in, carry_out; - - for (i = 0; i < AES_XTS_BLOCKSIZE; i++) - block[i] = data[i] ^ ctx->tweak[i]; - - if (do_encrypt) - rijndael_encrypt(&ctx->key1, block, data); - else - rijndael_decrypt(&ctx->key1, block, data); - - for (i = 0; i < AES_XTS_BLOCKSIZE; i++) - data[i] ^= ctx->tweak[i]; - - /* Exponentiate tweak */ - carry_in = 0; - for (i = 0; i < AES_XTS_BLOCKSIZE; i++) { - carry_out = ctx->tweak[i] & 0x80; - ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); - carry_in = carry_out; - } - if (carry_in) - ctx->tweak[0] ^= AES_XTS_ALPHA; - bzero(block, sizeof(block)); -} - -void -aes_xts_encrypt(caddr_t key, u_int8_t *data) -{ - aes_xts_crypt((struct aes_xts_ctx *)key, data, 1); -} - -void -aes_xts_decrypt(caddr_t key, u_int8_t *data) -{ - aes_xts_crypt((struct aes_xts_ctx *)key, data, 0); -} - -int -aes_xts_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - struct aes_xts_ctx *ctx; - - if (len != 32 && len != 64) - return EINVAL; - - *sched = malloc(sizeof(struct aes_xts_ctx), M_CRYPTO_DATA, - M_NOWAIT | M_ZERO); - if (*sched == NULL) - return ENOMEM; - ctx = (struct aes_xts_ctx *)*sched; - - rijndael_set_key(&ctx->key1, key, len * 4); - rijndael_set_key(&ctx->key2, key + (len / 2), len * 4); - - return 0; -} - -void -aes_xts_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(struct aes_xts_ctx)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -static void -cml_encrypt(caddr_t key, u_int8_t *blk) -{ - camellia_encrypt((camellia_ctx *) key, (u_char *) blk, (u_char *) blk); -} - -static void -cml_decrypt(caddr_t key, u_int8_t *blk) -{ - camellia_decrypt(((camellia_ctx *) key), (u_char *) blk, - (u_char *) blk); -} - -static int -cml_setkey(u_int8_t **sched, u_int8_t *key, int len) -{ - int err; - - if (len != 16 && len != 24 && len != 32) - return (EINVAL); - *sched = malloc(sizeof(camellia_ctx), M_CRYPTO_DATA, - M_NOWAIT|M_ZERO); - if (*sched != NULL) { - camellia_set_key((camellia_ctx *) *sched, (u_char *) key, - len * 8); - err = 0; - } else - err = ENOMEM; - return err; -} - -static void -cml_zerokey(u_int8_t **sched) -{ - bzero(*sched, sizeof(camellia_ctx)); - free(*sched, M_CRYPTO_DATA); - *sched = NULL; -} - -/* - * And now for auth. - */ - -static void -null_init(void *ctx) -{ -} - -static void -null_reinit(void *ctx, const u_int8_t *buf, u_int16_t len) -{ -} - -static int -null_update(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - return 0; -} - -static void -null_final(u_int8_t *buf, void *ctx) -{ - if (buf != (u_int8_t *) 0) - bzero(buf, 12); -} - -static int -RMD160Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - RMD160Update(ctx, buf, len); - return 0; -} - -static int -MD5Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - MD5Update(ctx, buf, len); - return 0; -} - -static void -SHA1Init_int(void *ctx) -{ - SHA1Init(ctx); -} - -static int -SHA1Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - SHA1Update(ctx, buf, len); - return 0; -} - -static void -SHA1Final_int(u_int8_t *blk, void *ctx) -{ - SHA1Final(blk, ctx); -} - -static int -SHA256Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - SHA256_Update(ctx, buf, len); - return 0; -} - -static int -SHA384Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - SHA384_Update(ctx, buf, len); - return 0; -} - -static int -SHA512Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) -{ - SHA512_Update(ctx, buf, len); - return 0; -} - -/* - * And compression - */ - -static u_int32_t -deflate_compress(data, size, out) - u_int8_t *data; - u_int32_t size; - u_int8_t **out; -{ - return deflate_global(data, size, 0, out); -} - -static u_int32_t -deflate_decompress(data, size, out) - u_int8_t *data; - u_int32_t size; - u_int8_t **out; -{ - return deflate_global(data, size, 1, out); -} Index: projects/clang380-import/sys/opencrypto/xform.h =================================================================== --- projects/clang380-import/sys/opencrypto/xform.h (revision 293015) +++ projects/clang380-import/sys/opencrypto/xform.h (revision 293016) @@ -1,124 +1,50 @@ /* $FreeBSD$ */ /* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ /*- * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) * * This code was written by Angelos D. Keromytis in Athens, Greece, in * February 2000. Network Security Technologies Inc. (NSTI) kindly * supported the development of this code. * * Copyright (c) 2000 Angelos D. Keromytis * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software without fee * is hereby granted, provided that this entire notice is included in * all source code copies of any software which is or includes a copy or * modification of this software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #ifndef _CRYPTO_XFORM_H_ #define _CRYPTO_XFORM_H_ #include #include #include #include #include #include #include -/* Declarations */ -struct auth_hash { - int type; - char *name; - u_int16_t keysize; - u_int16_t hashsize; - u_int16_t ctxsize; - u_int16_t blocksize; - void (*Init) (void *); - void (*Setkey) (void *, const u_int8_t *, u_int16_t); - void (*Reinit) (void *, const u_int8_t *, u_int16_t); - int (*Update) (void *, const u_int8_t *, u_int16_t); - void (*Final) (u_int8_t *, void *); -}; - -/* XXX use a define common with other hash stuff ! */ -#define AH_ALEN_MAX 64 /* max authenticator hash length */ - -struct enc_xform { - int type; - char *name; - u_int16_t blocksize; - u_int16_t ivsize; - u_int16_t minkey, maxkey; - void (*encrypt) (caddr_t, u_int8_t *); - void (*decrypt) (caddr_t, u_int8_t *); - int (*setkey) (u_int8_t **, u_int8_t *, int len); - void (*zerokey) (u_int8_t **); - void (*reinit) (caddr_t, u_int8_t *); -}; - -struct comp_algo { - int type; - char *name; - size_t minlen; - u_int32_t (*compress) (u_int8_t *, u_int32_t, u_int8_t **); - u_int32_t (*decompress) (u_int8_t *, u_int32_t, u_int8_t **); -}; - -union authctx { - MD5_CTX md5ctx; - SHA1_CTX sha1ctx; - RMD160_CTX rmd160ctx; - SHA256_CTX sha256ctx; - SHA384_CTX sha384ctx; - SHA512_CTX sha512ctx; - struct aes_gmac_ctx aes_gmac_ctx; -}; - -extern struct enc_xform enc_xform_null; -extern struct enc_xform enc_xform_des; -extern struct enc_xform enc_xform_3des; -extern struct enc_xform enc_xform_blf; -extern struct enc_xform enc_xform_cast5; -extern struct enc_xform enc_xform_skipjack; -extern struct enc_xform enc_xform_rijndael128; -extern struct enc_xform enc_xform_aes_icm; -extern struct enc_xform enc_xform_aes_nist_gcm; -extern struct enc_xform enc_xform_aes_nist_gmac; -extern struct enc_xform enc_xform_aes_xts; -extern struct enc_xform enc_xform_arc4; -extern struct enc_xform enc_xform_camellia; - -extern struct auth_hash auth_hash_null; -extern struct auth_hash auth_hash_key_md5; -extern struct auth_hash auth_hash_key_sha1; -extern struct auth_hash auth_hash_hmac_md5; -extern struct auth_hash auth_hash_hmac_sha1; -extern struct auth_hash auth_hash_hmac_ripemd_160; -extern struct auth_hash auth_hash_hmac_sha2_256; -extern struct auth_hash auth_hash_hmac_sha2_384; -extern struct auth_hash auth_hash_hmac_sha2_512; -extern struct auth_hash auth_hash_nist_gmac_aes_128; -extern struct auth_hash auth_hash_nist_gmac_aes_192; -extern struct auth_hash auth_hash_nist_gmac_aes_256; - -extern struct comp_algo comp_algo_deflate; +#include +#include +#include #ifdef _KERNEL #include MALLOC_DECLARE(M_XDATA); #endif #endif /* _CRYPTO_XFORM_H_ */ Index: projects/clang380-import/sys/opencrypto/xform_aes_icm.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_aes_icm.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_aes_icm.c (revision 293016) @@ -0,0 +1,152 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +static int aes_icm_setkey(u_int8_t **, u_int8_t *, int); +static void aes_icm_crypt(caddr_t, u_int8_t *); +static void aes_icm_zerokey(u_int8_t **); +static void aes_icm_reinit(caddr_t, u_int8_t *); +static void aes_gcm_reinit(caddr_t, u_int8_t *); + +/* Encryption instances */ +struct enc_xform enc_xform_aes_icm = { + CRYPTO_AES_ICM, "AES-ICM", + AES_BLOCK_LEN, AES_BLOCK_LEN, AES_MIN_KEY, AES_MAX_KEY, + aes_icm_crypt, + aes_icm_crypt, + aes_icm_setkey, + rijndael128_zerokey, + aes_icm_reinit, +}; + +struct enc_xform enc_xform_aes_nist_gcm = { + CRYPTO_AES_NIST_GCM_16, "AES-GCM", + AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY, + aes_icm_crypt, + aes_icm_crypt, + aes_icm_setkey, + aes_icm_zerokey, + aes_gcm_reinit, +}; + +/* + * Encryption wrapper routines. + */ +static void +aes_icm_reinit(caddr_t key, u_int8_t *iv) +{ + struct aes_icm_ctx *ctx; + + ctx = (struct aes_icm_ctx *)key; + bcopy(iv, ctx->ac_block, AESICM_BLOCKSIZE); +} + +static void +aes_gcm_reinit(caddr_t key, u_int8_t *iv) +{ + struct aes_icm_ctx *ctx; + + aes_icm_reinit(key, iv); + + ctx = (struct aes_icm_ctx *)key; + /* GCM starts with 2 as counter 1 is used for final xor of tag. */ + bzero(&ctx->ac_block[AESICM_BLOCKSIZE - 4], 4); + ctx->ac_block[AESICM_BLOCKSIZE - 1] = 2; +} + +static void +aes_icm_crypt(caddr_t key, u_int8_t *data) +{ + struct aes_icm_ctx *ctx; + u_int8_t keystream[AESICM_BLOCKSIZE]; + int i; + + ctx = (struct aes_icm_ctx *)key; + rijndaelEncrypt(ctx->ac_ek, ctx->ac_nr, ctx->ac_block, keystream); + for (i = 0; i < AESICM_BLOCKSIZE; i++) + data[i] ^= keystream[i]; + explicit_bzero(keystream, sizeof(keystream)); + + /* increment counter */ + for (i = AESICM_BLOCKSIZE - 1; + i >= 0; i--) + if (++ctx->ac_block[i]) /* continue on overflow */ + break; +} + +static int +aes_icm_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + struct aes_icm_ctx *ctx; + + if (len != 16 && len != 24 && len != 32) + return EINVAL; + + *sched = KMALLOC(sizeof(struct aes_icm_ctx), M_CRYPTO_DATA, + M_NOWAIT | M_ZERO); + if (*sched == NULL) + return ENOMEM; + + ctx = (struct aes_icm_ctx *)*sched; + ctx->ac_nr = rijndaelKeySetupEnc(ctx->ac_ek, (u_char *)key, len * 8); + return 0; +} + +static void +aes_icm_zerokey(u_int8_t **sched) +{ + + bzero(*sched, sizeof(struct aes_icm_ctx)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_aes_icm.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_aes_xts.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_aes_xts.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_aes_xts.c (revision 293016) @@ -0,0 +1,164 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +static int aes_xts_setkey(u_int8_t **, u_int8_t *, int); +static void aes_xts_encrypt(caddr_t, u_int8_t *); +static void aes_xts_decrypt(caddr_t, u_int8_t *); +static void aes_xts_zerokey(u_int8_t **); +static void aes_xts_reinit(caddr_t, u_int8_t *); + +/* Encryption instances */ +struct enc_xform enc_xform_aes_xts = { + CRYPTO_AES_XTS, "AES-XTS", + AES_BLOCK_LEN, AES_XTS_IV_LEN, AES_XTS_MIN_KEY, AES_XTS_MAX_KEY, + aes_xts_encrypt, + aes_xts_decrypt, + aes_xts_setkey, + aes_xts_zerokey, + aes_xts_reinit +}; + +/* + * Encryption wrapper routines. + */ +static void +aes_xts_reinit(caddr_t key, u_int8_t *iv) +{ + struct aes_xts_ctx *ctx = (struct aes_xts_ctx *)key; + u_int64_t blocknum; + u_int i; + + /* + * Prepare tweak as E_k2(IV). IV is specified as LE representation + * of a 64-bit block number which we allow to be passed in directly. + */ + bcopy(iv, &blocknum, AES_XTS_IVSIZE); + for (i = 0; i < AES_XTS_IVSIZE; i++) { + ctx->tweak[i] = blocknum & 0xff; + blocknum >>= 8; + } + /* Last 64 bits of IV are always zero */ + bzero(ctx->tweak + AES_XTS_IVSIZE, AES_XTS_IVSIZE); + + rijndael_encrypt(&ctx->key2, ctx->tweak, ctx->tweak); +} + +static void +aes_xts_crypt(struct aes_xts_ctx *ctx, u_int8_t *data, u_int do_encrypt) +{ + u_int8_t block[AES_XTS_BLOCKSIZE]; + u_int i, carry_in, carry_out; + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + block[i] = data[i] ^ ctx->tweak[i]; + + if (do_encrypt) + rijndael_encrypt(&ctx->key1, block, data); + else + rijndael_decrypt(&ctx->key1, block, data); + + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) + data[i] ^= ctx->tweak[i]; + + /* Exponentiate tweak */ + carry_in = 0; + for (i = 0; i < AES_XTS_BLOCKSIZE; i++) { + carry_out = ctx->tweak[i] & 0x80; + ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); + carry_in = carry_out; + } + if (carry_in) + ctx->tweak[0] ^= AES_XTS_ALPHA; + bzero(block, sizeof(block)); +} + +static void +aes_xts_encrypt(caddr_t key, u_int8_t *data) +{ + aes_xts_crypt((struct aes_xts_ctx *)key, data, 1); +} + +static void +aes_xts_decrypt(caddr_t key, u_int8_t *data) +{ + aes_xts_crypt((struct aes_xts_ctx *)key, data, 0); +} + +static int +aes_xts_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + struct aes_xts_ctx *ctx; + + if (len != 32 && len != 64) + return EINVAL; + + *sched = KMALLOC(sizeof(struct aes_xts_ctx), M_CRYPTO_DATA, + M_NOWAIT | M_ZERO); + if (*sched == NULL) + return ENOMEM; + ctx = (struct aes_xts_ctx *)*sched; + + rijndael_set_key(&ctx->key1, key, len * 4); + rijndael_set_key(&ctx->key2, key + (len / 2), len * 4); + + return 0; +} + +static void +aes_xts_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(struct aes_xts_ctx)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_aes_xts.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_auth.h =================================================================== --- projects/clang380-import/sys/opencrypto/xform_auth.h (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_auth.h (revision 293016) @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ +/* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ + +/*- + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#ifndef _CRYPTO_XFORM_AUTH_H_ +#define _CRYPTO_XFORM_AUTH_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* XXX use a define common with other hash stuff ! */ +#define AH_ALEN_MAX 64 /* max authenticator hash length */ + +/* Declarations */ +struct auth_hash { + int type; + char *name; + u_int16_t keysize; + u_int16_t hashsize; + u_int16_t ctxsize; + u_int16_t blocksize; + void (*Init) (void *); + void (*Setkey) (void *, const u_int8_t *, u_int16_t); + void (*Reinit) (void *, const u_int8_t *, u_int16_t); + int (*Update) (void *, const u_int8_t *, u_int16_t); + void (*Final) (u_int8_t *, void *); +}; + +extern struct auth_hash auth_hash_null; +extern struct auth_hash auth_hash_key_md5; +extern struct auth_hash auth_hash_key_sha1; +extern struct auth_hash auth_hash_hmac_md5; +extern struct auth_hash auth_hash_hmac_sha1; +extern struct auth_hash auth_hash_hmac_ripemd_160; +extern struct auth_hash auth_hash_hmac_sha2_256; +extern struct auth_hash auth_hash_hmac_sha2_384; +extern struct auth_hash auth_hash_hmac_sha2_512; +extern struct auth_hash auth_hash_nist_gmac_aes_128; +extern struct auth_hash auth_hash_nist_gmac_aes_192; +extern struct auth_hash auth_hash_nist_gmac_aes_256; + +union authctx { + MD5_CTX md5ctx; + SHA1_CTX sha1ctx; + RMD160_CTX rmd160ctx; + SHA256_CTX sha256ctx; + SHA384_CTX sha384ctx; + SHA512_CTX sha512ctx; + struct aes_gmac_ctx aes_gmac_ctx; +}; + +#endif /* _CRYPTO_XFORM_AUTH_H_ */ Property changes on: projects/clang380-import/sys/opencrypto/xform_auth.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_blf.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_blf.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_blf.c (revision 293016) @@ -0,0 +1,127 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int blf_setkey(u_int8_t **, u_int8_t *, int); +static void blf_encrypt(caddr_t, u_int8_t *); +static void blf_decrypt(caddr_t, u_int8_t *); +static void blf_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_blf = { + CRYPTO_BLF_CBC, "Blowfish", + BLOWFISH_BLOCK_LEN, BLOWFISH_BLOCK_LEN, BLOWFISH_MIN_KEY, + BLOWFISH_MAX_KEY, + blf_encrypt, + blf_decrypt, + blf_setkey, + blf_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +blf_encrypt(caddr_t key, u_int8_t *blk) +{ + BF_LONG t[2]; + + memcpy(t, blk, sizeof (t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + /* NB: BF_encrypt expects the block in host order! */ + BF_encrypt(t, (BF_KEY *) key); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + memcpy(blk, t, sizeof (t)); +} + +static void +blf_decrypt(caddr_t key, u_int8_t *blk) +{ + BF_LONG t[2]; + + memcpy(t, blk, sizeof (t)); + t[0] = ntohl(t[0]); + t[1] = ntohl(t[1]); + /* NB: BF_decrypt expects the block in host order! */ + BF_decrypt(t, (BF_KEY *) key); + t[0] = htonl(t[0]); + t[1] = htonl(t[1]); + memcpy(blk, t, sizeof (t)); +} + +static int +blf_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + int err; + + *sched = KMALLOC(sizeof(BF_KEY), + M_CRYPTO_DATA, M_NOWAIT|M_ZERO); + if (*sched != NULL) { + BF_set_key((BF_KEY *) *sched, len, key); + err = 0; + } else + err = ENOMEM; + return err; +} + +static void +blf_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(BF_KEY)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_blf.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_cast5.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_cast5.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_cast5.c (revision 293016) @@ -0,0 +1,107 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int cast5_setkey(u_int8_t **, u_int8_t *, int); +static void cast5_encrypt(caddr_t, u_int8_t *); +static void cast5_decrypt(caddr_t, u_int8_t *); +static void cast5_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_cast5 = { + CRYPTO_CAST_CBC, "CAST-128", + CAST128_BLOCK_LEN, CAST128_BLOCK_LEN, CAST_MIN_KEY, CAST_MAX_KEY, + cast5_encrypt, + cast5_decrypt, + cast5_setkey, + cast5_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +cast5_encrypt(caddr_t key, u_int8_t *blk) +{ + cast_encrypt((cast_key *) key, blk, blk); +} + +static void +cast5_decrypt(caddr_t key, u_int8_t *blk) +{ + cast_decrypt((cast_key *) key, blk, blk); +} + +static int +cast5_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + int err; + + *sched = KMALLOC(sizeof(cast_key), M_CRYPTO_DATA, M_NOWAIT|M_ZERO); + if (*sched != NULL) { + cast_setkey((cast_key *)*sched, key, len); + err = 0; + } else + err = ENOMEM; + return err; +} + +static void +cast5_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(cast_key)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_cast5.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_cml.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_cml.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_cml.c (revision 293016) @@ -0,0 +1,113 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int cml_setkey(u_int8_t **, u_int8_t *, int); +static void cml_encrypt(caddr_t, u_int8_t *); +static void cml_decrypt(caddr_t, u_int8_t *); +static void cml_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_camellia = { + CRYPTO_CAMELLIA_CBC, "Camellia", + CAMELLIA_BLOCK_LEN, CAMELLIA_BLOCK_LEN, CAMELLIA_MIN_KEY, + CAMELLIA_MAX_KEY, + cml_encrypt, + cml_decrypt, + cml_setkey, + cml_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +cml_encrypt(caddr_t key, u_int8_t *blk) +{ + camellia_encrypt((camellia_ctx *) key, (u_char *) blk, (u_char *) blk); +} + +static void +cml_decrypt(caddr_t key, u_int8_t *blk) +{ + camellia_decrypt(((camellia_ctx *) key), (u_char *) blk, + (u_char *) blk); +} + +static int +cml_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + int err; + + if (len != 16 && len != 24 && len != 32) + return (EINVAL); + *sched = KMALLOC(sizeof(camellia_ctx), M_CRYPTO_DATA, + M_NOWAIT|M_ZERO); + if (*sched != NULL) { + camellia_set_key((camellia_ctx *) *sched, (u_char *) key, + len * 8); + err = 0; + } else + err = ENOMEM; + return err; +} + +static void +cml_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(camellia_ctx)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_cml.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_comp.h =================================================================== --- projects/clang380-import/sys/opencrypto/xform_comp.h (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_comp.h (revision 293016) @@ -0,0 +1,52 @@ +/* $FreeBSD$ */ +/* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ + +/*- + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#ifndef _CRYPTO_XFORM_COMP_H_ +#define _CRYPTO_XFORM_COMP_H_ + +#include +#include + +#include +#include +#include + +/* Declarations */ +struct comp_algo { + int type; + char *name; + size_t minlen; + u_int32_t (*compress) (u_int8_t *, u_int32_t, u_int8_t **); + u_int32_t (*decompress) (u_int8_t *, u_int32_t, u_int8_t **); +}; + +extern struct comp_algo comp_algo_deflate; + +#endif /* _CRYPTO_XFORM_COMP_H_ */ Property changes on: projects/clang380-import/sys/opencrypto/xform_comp.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_deflate.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_deflate.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_deflate.c (revision 293016) @@ -0,0 +1,86 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static u_int32_t deflate_compress(u_int8_t *, u_int32_t, u_int8_t **); +static u_int32_t deflate_decompress(u_int8_t *, u_int32_t, u_int8_t **); + +/* Compression instance */ +struct comp_algo comp_algo_deflate = { + CRYPTO_DEFLATE_COMP, "Deflate", + 90, deflate_compress, + deflate_decompress +}; + +/* + * And compression + */ + +static u_int32_t +deflate_compress(data, size, out) + u_int8_t *data; + u_int32_t size; + u_int8_t **out; +{ + return deflate_global(data, size, 0, out); +} + +static u_int32_t +deflate_decompress(data, size, out) + u_int8_t *data; + u_int32_t size; + u_int8_t **out; +{ + return deflate_global(data, size, 1, out); +} Property changes on: projects/clang380-import/sys/opencrypto/xform_deflate.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_des1.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_des1.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_des1.c (revision 293016) @@ -0,0 +1,116 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int des1_setkey(u_int8_t **, u_int8_t *, int); +static void des1_encrypt(caddr_t, u_int8_t *); +static void des1_decrypt(caddr_t, u_int8_t *); +static void des1_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_des = { + CRYPTO_DES_CBC, "DES", + DES_BLOCK_LEN, DES_BLOCK_LEN, DES_MIN_KEY, DES_MAX_KEY, + des1_encrypt, + des1_decrypt, + des1_setkey, + des1_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +des1_encrypt(caddr_t key, u_int8_t *blk) +{ + des_cblock *cb = (des_cblock *) blk; + des_key_schedule *p = (des_key_schedule *) key; + + des_ecb_encrypt(cb, cb, p[0], DES_ENCRYPT); +} + +static void +des1_decrypt(caddr_t key, u_int8_t *blk) +{ + des_cblock *cb = (des_cblock *) blk; + des_key_schedule *p = (des_key_schedule *) key; + + des_ecb_encrypt(cb, cb, p[0], DES_DECRYPT); +} + +static int +des1_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + des_key_schedule *p; + int err; + + p = KMALLOC(sizeof (des_key_schedule), + M_CRYPTO_DATA, M_NOWAIT|M_ZERO); + if (p != NULL) { + des_set_key((des_cblock *) key, p[0]); + err = 0; + } else + err = ENOMEM; + *sched = (u_int8_t *) p; + return err; +} + +static void +des1_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof (des_key_schedule)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_des1.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_des3.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_des3.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_des3.c (revision 293016) @@ -0,0 +1,119 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int des3_setkey(u_int8_t **, u_int8_t *, int); +static void des3_encrypt(caddr_t, u_int8_t *); +static void des3_decrypt(caddr_t, u_int8_t *); +static void des3_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_3des = { + CRYPTO_3DES_CBC, "3DES", + DES3_BLOCK_LEN, DES3_BLOCK_LEN, TRIPLE_DES_MIN_KEY, + TRIPLE_DES_MAX_KEY, + des3_encrypt, + des3_decrypt, + des3_setkey, + des3_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +des3_encrypt(caddr_t key, u_int8_t *blk) +{ + des_cblock *cb = (des_cblock *) blk; + des_key_schedule *p = (des_key_schedule *) key; + + des_ecb3_encrypt(cb, cb, p[0], p[1], p[2], DES_ENCRYPT); +} + +static void +des3_decrypt(caddr_t key, u_int8_t *blk) +{ + des_cblock *cb = (des_cblock *) blk; + des_key_schedule *p = (des_key_schedule *) key; + + des_ecb3_encrypt(cb, cb, p[0], p[1], p[2], DES_DECRYPT); +} + +static int +des3_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + des_key_schedule *p; + int err; + + p = KMALLOC(3*sizeof (des_key_schedule), + M_CRYPTO_DATA, M_NOWAIT|M_ZERO); + if (p != NULL) { + des_set_key((des_cblock *)(key + 0), p[0]); + des_set_key((des_cblock *)(key + 8), p[1]); + des_set_key((des_cblock *)(key + 16), p[2]); + err = 0; + } else + err = ENOMEM; + *sched = (u_int8_t *) p; + return err; +} + +static void +des3_zerokey(u_int8_t **sched) +{ + bzero(*sched, 3*sizeof (des_key_schedule)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_des3.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_enc.h =================================================================== --- projects/clang380-import/sys/opencrypto/xform_enc.h (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_enc.h (revision 293016) @@ -0,0 +1,92 @@ +/* $FreeBSD$ */ +/* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ + +/*- + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * This code was written by Angelos D. Keromytis in Athens, Greece, in + * February 2000. Network Security Technologies Inc. (NSTI) kindly + * supported the development of this code. + * + * Copyright (c) 2000 Angelos D. Keromytis + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all source code copies of any software which is or includes a copy or + * modification of this software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#ifndef _CRYPTO_XFORM_ENC_H_ +#define _CRYPTO_XFORM_ENC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AESICM_BLOCKSIZE AES_BLOCK_LEN +#define AES_XTS_BLOCKSIZE 16 +#define AES_XTS_IVSIZE 8 +#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ + +/* Declarations */ +struct enc_xform { + int type; + char *name; + u_int16_t blocksize; + u_int16_t ivsize; + u_int16_t minkey, maxkey; + void (*encrypt) (caddr_t, u_int8_t *); + void (*decrypt) (caddr_t, u_int8_t *); + int (*setkey) (u_int8_t **, u_int8_t *, int len); + void (*zerokey) (u_int8_t **); + void (*reinit) (caddr_t, u_int8_t *); +}; + + +extern struct enc_xform enc_xform_null; +extern struct enc_xform enc_xform_des; +extern struct enc_xform enc_xform_3des; +extern struct enc_xform enc_xform_blf; +extern struct enc_xform enc_xform_cast5; +extern struct enc_xform enc_xform_skipjack; +extern struct enc_xform enc_xform_rijndael128; +extern struct enc_xform enc_xform_aes_icm; +extern struct enc_xform enc_xform_aes_nist_gcm; +extern struct enc_xform enc_xform_aes_nist_gmac; +extern struct enc_xform enc_xform_aes_xts; +extern struct enc_xform enc_xform_arc4; +extern struct enc_xform enc_xform_camellia; + +struct aes_icm_ctx { + u_int32_t ac_ek[4*(RIJNDAEL_MAXNR + 1)]; + /* ac_block is initalized to IV */ + u_int8_t ac_block[AESICM_BLOCKSIZE]; + int ac_nr; +}; + +struct aes_xts_ctx { + rijndael_ctx key1; + rijndael_ctx key2; + u_int8_t tweak[AES_XTS_BLOCKSIZE]; +}; + +#endif /* _CRYPTO_XFORM_ENC_H_ */ Property changes on: projects/clang380-import/sys/opencrypto/xform_enc.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_gmac.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_gmac.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_gmac.c (revision 293016) @@ -0,0 +1,99 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +/* Encryption instances */ +struct enc_xform enc_xform_aes_nist_gmac = { + CRYPTO_AES_NIST_GMAC, "AES-GMAC", + AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +/* Authentication instances */ +struct auth_hash auth_hash_nist_gmac_aes_128 = { + CRYPTO_AES_128_NIST_GMAC, "GMAC-AES-128", + AES_128_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), + GMAC_BLOCK_LEN, + (void (*)(void *)) AES_GMAC_Init, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, + (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, + (void (*)(u_int8_t *, void *)) AES_GMAC_Final +}; + +struct auth_hash auth_hash_nist_gmac_aes_192 = { + CRYPTO_AES_192_NIST_GMAC, "GMAC-AES-192", + AES_192_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), + GMAC_BLOCK_LEN, + (void (*)(void *)) AES_GMAC_Init, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, + (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, + (void (*)(u_int8_t *, void *)) AES_GMAC_Final +}; + +struct auth_hash auth_hash_nist_gmac_aes_256 = { + CRYPTO_AES_256_NIST_GMAC, "GMAC-AES-256", + AES_256_GMAC_KEY_LEN, AES_GMAC_HASH_LEN, sizeof(struct aes_gmac_ctx), + GMAC_BLOCK_LEN, + (void (*)(void *)) AES_GMAC_Init, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey, + (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit, + (int (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Update, + (void (*)(u_int8_t *, void *)) AES_GMAC_Final +}; Property changes on: projects/clang380-import/sys/opencrypto/xform_gmac.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_md5.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_md5.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_md5.c (revision 293016) @@ -0,0 +1,81 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int MD5Update_int(void *, const u_int8_t *, u_int16_t); + +/* Authentication instances */ +struct auth_hash auth_hash_hmac_md5 = { + CRYPTO_MD5_HMAC, "HMAC-MD5", + MD5_HMAC_KEY_LEN, MD5_HASH_LEN, sizeof(MD5_CTX), MD5_HMAC_BLOCK_LEN, + (void (*) (void *)) MD5Init, NULL, NULL, MD5Update_int, + (void (*) (u_int8_t *, void *)) MD5Final +}; + +struct auth_hash auth_hash_key_md5 = { + CRYPTO_MD5_KPDK, "Keyed MD5", + NULL_HMAC_KEY_LEN, MD5_KPDK_HASH_LEN, sizeof(MD5_CTX), 0, + (void (*)(void *)) MD5Init, NULL, NULL, MD5Update_int, + (void (*)(u_int8_t *, void *)) MD5Final +}; + +/* + * And now for auth. + */ +static int +MD5Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + MD5Update(ctx, buf, len); + return 0; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_md5.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_null.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_null.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_null.c (revision 293016) @@ -0,0 +1,136 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int null_setkey(u_int8_t **, u_int8_t *, int); +static void null_encrypt(caddr_t, u_int8_t *); +static void null_decrypt(caddr_t, u_int8_t *); +static void null_zerokey(u_int8_t **); + +static void null_init(void *); +static void null_reinit(void *ctx, const u_int8_t *buf, u_int16_t len); +static int null_update(void *, const u_int8_t *, u_int16_t); +static void null_final(u_int8_t *, void *); + +/* Encryption instances */ +struct enc_xform enc_xform_null = { + CRYPTO_NULL_CBC, "NULL", + /* NB: blocksize of 4 is to generate a properly aligned ESP header */ + NULL_BLOCK_LEN, 0, NULL_MIN_KEY, NULL_MAX_KEY, + null_encrypt, + null_decrypt, + null_setkey, + null_zerokey, + NULL, +}; + +/* Authentication instances */ +struct auth_hash auth_hash_null = { /* NB: context isn't used */ + CRYPTO_NULL_HMAC, "NULL-HMAC", + NULL_HMAC_KEY_LEN, NULL_HASH_LEN, sizeof(int), NULL_HMAC_BLOCK_LEN, + null_init, null_reinit, null_reinit, null_update, null_final +}; + +/* + * Encryption wrapper routines. + */ +static void +null_encrypt(caddr_t key, u_int8_t *blk) +{ +} + +static void +null_decrypt(caddr_t key, u_int8_t *blk) +{ +} + +static int +null_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + *sched = NULL; + return 0; +} + +static void +null_zerokey(u_int8_t **sched) +{ + *sched = NULL; +} + +/* + * And now for auth. + */ + +static void +null_init(void *ctx) +{ +} + +static void +null_reinit(void *ctx, const u_int8_t *buf, u_int16_t len) +{ +} + +static int +null_update(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + return 0; +} + +static void +null_final(u_int8_t *buf, void *ctx) +{ + if (buf != (u_int8_t *) 0) + bzero(buf, 12); +} Property changes on: projects/clang380-import/sys/opencrypto/xform_null.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_rijndael.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_rijndael.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_rijndael.c (revision 293016) @@ -0,0 +1,113 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int rijndael128_setkey(u_int8_t **, u_int8_t *, int); +static void rijndael128_encrypt(caddr_t, u_int8_t *); +static void rijndael128_decrypt(caddr_t, u_int8_t *); +static void rijndael128_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_rijndael128 = { + CRYPTO_RIJNDAEL128_CBC, "Rijndael-128/AES", + RIJNDAEL128_BLOCK_LEN, RIJNDAEL128_BLOCK_LEN, RIJNDAEL_MIN_KEY, + RIJNDAEL_MAX_KEY, + rijndael128_encrypt, + rijndael128_decrypt, + rijndael128_setkey, + rijndael128_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +rijndael128_encrypt(caddr_t key, u_int8_t *blk) +{ + rijndael_encrypt((rijndael_ctx *) key, (u_char *) blk, (u_char *) blk); +} + +static void +rijndael128_decrypt(caddr_t key, u_int8_t *blk) +{ + rijndael_decrypt(((rijndael_ctx *) key), (u_char *) blk, + (u_char *) blk); +} + +static int +rijndael128_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + int err; + + if (len != 16 && len != 24 && len != 32) + return (EINVAL); + *sched = KMALLOC(sizeof(rijndael_ctx), M_CRYPTO_DATA, + M_NOWAIT|M_ZERO); + if (*sched != NULL) { + rijndael_set_key((rijndael_ctx *) *sched, (u_char *) key, + len * 8); + err = 0; + } else + err = ENOMEM; + return err; +} + +static void +rijndael128_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(rijndael_ctx)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_rijndael.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_rmd160.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_rmd160.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_rmd160.c (revision 293016) @@ -0,0 +1,75 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int RMD160Update_int(void *, const u_int8_t *, u_int16_t); + +/* Authentication instances */ +struct auth_hash auth_hash_hmac_ripemd_160 = { + CRYPTO_RIPEMD160_HMAC, "HMAC-RIPEMD-160", + RIPEMD160_HMAC_KEY_LEN, RIPEMD160_HASH_LEN, sizeof(RMD160_CTX), + RIPEMD160_HMAC_BLOCK_LEN, + (void (*)(void *)) RMD160Init, NULL, NULL, RMD160Update_int, + (void (*)(u_int8_t *, void *)) RMD160Final +}; + +/* + * And now for auth. + */ +static int +RMD160Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + RMD160Update(ctx, buf, len); + return 0; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_rmd160.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_sha1.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_sha1.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_sha1.c (revision 293016) @@ -0,0 +1,93 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static void SHA1Init_int(void *); +static int SHA1Update_int(void *, const u_int8_t *, u_int16_t); +static void SHA1Final_int(u_int8_t *, void *); + +/* Authentication instances */ +struct auth_hash auth_hash_hmac_sha1 = { + CRYPTO_SHA1_HMAC, "HMAC-SHA1", + SHA1_HMAC_KEY_LEN, SHA1_HASH_LEN, sizeof(SHA1_CTX), SHA1_HMAC_BLOCK_LEN, + SHA1Init_int, NULL, NULL, SHA1Update_int, SHA1Final_int +}; + +struct auth_hash auth_hash_key_sha1 = { + CRYPTO_SHA1_KPDK, "Keyed SHA1", + NULL_HMAC_KEY_LEN, SHA1_KPDK_HASH_LEN, sizeof(SHA1_CTX), 0, + SHA1Init_int, NULL, NULL, SHA1Update_int, SHA1Final_int +}; + +/* + * And now for auth. + */ +static void +SHA1Init_int(void *ctx) +{ + SHA1Init(ctx); +} + +static int +SHA1Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + SHA1Update(ctx, buf, len); + return 0; +} + +static void +SHA1Final_int(u_int8_t *blk, void *ctx) +{ + SHA1Final(blk, ctx); +} Property changes on: projects/clang380-import/sys/opencrypto/xform_sha1.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_sha2.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_sha2.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_sha2.c (revision 293016) @@ -0,0 +1,109 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +static int SHA256Update_int(void *, const u_int8_t *, u_int16_t); +static int SHA384Update_int(void *, const u_int8_t *, u_int16_t); +static int SHA512Update_int(void *, const u_int8_t *, u_int16_t); + +/* Authentication instances */ +struct auth_hash auth_hash_hmac_sha2_256 = { + CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256", + SHA2_256_HMAC_KEY_LEN, SHA2_256_HASH_LEN, sizeof(SHA256_CTX), + SHA2_256_HMAC_BLOCK_LEN, + (void (*)(void *)) SHA256_Init, NULL, NULL, SHA256Update_int, + (void (*)(u_int8_t *, void *)) SHA256_Final +}; + +struct auth_hash auth_hash_hmac_sha2_384 = { + CRYPTO_SHA2_384_HMAC, "HMAC-SHA2-384", + SHA2_384_HMAC_KEY_LEN, SHA2_384_HASH_LEN, sizeof(SHA384_CTX), + SHA2_384_HMAC_BLOCK_LEN, + (void (*)(void *)) SHA384_Init, NULL, NULL, SHA384Update_int, + (void (*)(u_int8_t *, void *)) SHA384_Final +}; + +struct auth_hash auth_hash_hmac_sha2_512 = { + CRYPTO_SHA2_512_HMAC, "HMAC-SHA2-512", + SHA2_512_HMAC_KEY_LEN, SHA2_512_HASH_LEN, sizeof(SHA512_CTX), + SHA2_512_HMAC_BLOCK_LEN, + (void (*)(void *)) SHA512_Init, NULL, NULL, SHA512Update_int, + (void (*)(u_int8_t *, void *)) SHA512_Final +}; + +/* + * And now for auth. + */ +static int +SHA256Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + SHA256_Update(ctx, buf, len); + return 0; +} + +static int +SHA384Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + SHA384_Update(ctx, buf, len); + return 0; +} + +static int +SHA512Update_int(void *ctx, const u_int8_t *buf, u_int16_t len) +{ + SHA512_Update(ctx, buf, len); + return 0; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_sha2.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_skipjack.c =================================================================== --- projects/clang380-import/sys/opencrypto/xform_skipjack.c (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_skipjack.c (revision 293016) @@ -0,0 +1,117 @@ +/* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ +/*- + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr), + * Niels Provos (provos@physnet.uni-hamburg.de) and + * Damien Miller (djm@mindrot.org). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Additional features in 1999 by Angelos D. Keromytis. + * + * AES XTS implementation in 2008 by Damien Miller + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Angelos D. Keromytis and Niels Provos. + * + * Copyright (C) 2001, Angelos D. Keromytis. + * + * Copyright (C) 2008, Damien Miller + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by John-Mark Gurney + * under sponsorship of the FreeBSD Foundation and + * Rubicon Communications, LLC (Netgate). + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +static int skipjack_setkey(u_int8_t **, u_int8_t *, int); +static void skipjack_encrypt(caddr_t, u_int8_t *); +static void skipjack_decrypt(caddr_t, u_int8_t *); +static void skipjack_zerokey(u_int8_t **); + +/* Encryption instances */ +struct enc_xform enc_xform_skipjack = { + CRYPTO_SKIPJACK_CBC, "Skipjack", + SKIPJACK_BLOCK_LEN, SKIPJACK_BLOCK_LEN, SKIPJACK_MIN_KEY, + SKIPJACK_MAX_KEY, + skipjack_encrypt, + skipjack_decrypt, skipjack_setkey, + skipjack_zerokey, + NULL, +}; + +/* + * Encryption wrapper routines. + */ +static void +skipjack_encrypt(caddr_t key, u_int8_t *blk) +{ + skipjack_forwards(blk, blk, (u_int8_t **) key); +} + +static void +skipjack_decrypt(caddr_t key, u_int8_t *blk) +{ + skipjack_backwards(blk, blk, (u_int8_t **) key); +} + +static int +skipjack_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + int err; + + /* NB: allocate all the memory that's needed at once */ + *sched = KMALLOC(10 * (sizeof(u_int8_t *) + 0x100), + M_CRYPTO_DATA, M_NOWAIT|M_ZERO); + if (*sched != NULL) { + u_int8_t** key_tables = (u_int8_t**) *sched; + u_int8_t* table = (u_int8_t*) &key_tables[10]; + int k; + + for (k = 0; k < 10; k++) { + key_tables[k] = table; + table += 0x100; + } + subkey_table_gen(key, (u_int8_t **) *sched); + err = 0; + } else + err = ENOMEM; + return err; +} + +static void +skipjack_zerokey(u_int8_t **sched) +{ + bzero(*sched, 10 * (sizeof(u_int8_t *) + 0x100)); + KFREE(*sched, M_CRYPTO_DATA); + *sched = NULL; +} Property changes on: projects/clang380-import/sys/opencrypto/xform_skipjack.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: projects/clang380-import/sys/opencrypto/xform_userland.h =================================================================== --- projects/clang380-import/sys/opencrypto/xform_userland.h (nonexistent) +++ projects/clang380-import/sys/opencrypto/xform_userland.h (revision 293016) @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2015 Allan Jude + * 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 _CRYPTO_XFORM_USERLAND_H_ +#define _CRYPTO_XFORM_USERLAND_H_ + +#ifdef _KERNEL +#include +#define KMALLOC(size, type, flags) malloc(size, type, flags) +#define KFREE(ptr, type) free(ptr, type) +#else /* not _KERNEL */ +#ifdef _STAND +#include +#else /* !_STAND */ +#include +#include +#endif /* _STAND */ +#define KMALLOC(size, type, flags) malloc(size) +#define KFREE(ptr, type) free(ptr) +#endif /* _KERNEL */ + + +#endif /* _CRYPTO_XFORM_USERLAND_H_ */ Property changes on: projects/clang380-import/sys/opencrypto/xform_userland.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang380-import/sys/sparc64/include/ktr.h =================================================================== --- projects/clang380-import/sys/sparc64/include/ktr.h (revision 293015) +++ projects/clang380-import/sys/sparc64/include/ktr.h (revision 293016) @@ -1,99 +1,99 @@ /*- * Copyright (c) 1996 Berkeley Software Design, 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. * 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. Berkeley Software Design Inc's name may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from BSDI $Id: ktr.h,v 1.10.2.7 2000/03/16 21:44:42 cp Exp $ * $FreeBSD$ */ #ifndef _MACHINE_KTR_H_ #define _MACHINE_KTR_H_ #include #ifdef LOCORE /* * XXX could really use another register ... */ #define ATR(desc, r1, r2, r3, l1, l2) \ .sect .rodata ; \ l1: .asciz desc ; \ .previous ; \ SET(ktr_idx, r2, r1) ; \ lduw [r1], r2 ; \ l2: add r2, 1, r3 ; \ set KTR_ENTRIES - 1, r1 ; \ and r3, r1, r3 ; \ set ktr_idx, r1 ; \ casa [r1] ASI_N, r2, r3 ; \ cmp r2, r3 ; \ bne %icc, l2 ## b ; \ mov r3, r2 ; \ SET(ktr_buf, r3, r1) ; \ ldx [r1], r1 ; \ mulx r2, KTR_SIZEOF, r2 ; \ add r1, r2, r1 ; \ rd %tick, r2 ; \ stx r2, [r1 + KTR_TIMESTAMP] ; \ - lduw [PCPU(MID)], r2 ; \ + lduw [PCPU(CPUID)], r2 ; \ stw r2, [r1 + KTR_CPU] ; \ stw %g0, [r1 + KTR_LINE] ; \ stx %g0, [r1 + KTR_FILE] ; \ SET(l1 ## b, r3, r2) ; \ stx r2, [r1 + KTR_DESC] #define CATR(mask, desc, r1, r2, r3, l1, l2, l3) \ setx mask, r3, r1 ; \ setx ktr_mask, r3, r2 ; \ ldx [r2], r2 ; \ and r2, r1, r1 ; \ brz r1, l3 ## f ; \ nop ; \ lduw [PCPU(CPUID)], r2 ; \ mov _NCPUBITS, r3 ; \ udivx r2, r3, r2 ; \ srl r2, 0, r2 ; \ sllx r2, PTR_SHIFT, r2 ; \ SET(ktr_cpumask, r3, r1) ; \ ldx [r1 + r2], r1 ; \ lduw [PCPU(CPUID)], r2 ; \ mov _NCPUBITS, r3 ; \ udivx r2, r3, r2 ; \ srl r2, 0, r2 ; \ smul r2, r3, r3 ; \ lduw [PCPU(CPUID)], r2 ; \ sub r2, r3, r3 ; \ mov 1, r2 ; \ sllx r2, r3, r2 ; \ andn r1, r2, r1 ; \ brz r1, l3 ## f ; \ nop ; \ ATR(desc, r1, r2, r3, l1, l2) #endif /* LOCORE */ #endif /* !_MACHINE_KTR_H_ */ Index: projects/clang380-import/sys/sys/copyright.h =================================================================== --- projects/clang380-import/sys/sys/copyright.h (revision 293015) +++ projects/clang380-import/sys/sys/copyright.h (revision 293016) @@ -1,57 +1,57 @@ /*- - * Copyright (C) 1992-2015 The FreeBSD Project. All rights reserved. + * Copyright (C) 1992-2016 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 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 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$ */ /* Copyrights macros */ /* Add a FreeBSD vendor copyright here */ #define COPYRIGHT_Vendor \ "" /* FreeBSD */ #define COPYRIGHT_FreeBSD \ - "Copyright (c) 1992-2015 The FreeBSD Project.\n" + "Copyright (c) 1992-2016 The FreeBSD Project.\n" /* Foundation */ #define TRADEMARK_Foundation \ "FreeBSD is a registered trademark of The FreeBSD Foundation.\n" /* Berkeley */ #define COPYRIGHT_UCB \ "Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994\n\tThe Regents of the University of California. All rights reserved.\n" /* a port of FreeBSD to the NEC PC98, Japan */ #if defined(PC98) #define COPYRIGHT_PC98 \ "Copyright (c) 1994-2003 FreeBSD(98) porting team.\nCopyright (c) 1992 A.Kojima F.Ukai M.Ishii (KMC).\n" #else #define COPYRIGHT_PC98 #endif char copyright[] = COPYRIGHT_Vendor COPYRIGHT_FreeBSD COPYRIGHT_PC98 \ COPYRIGHT_UCB; char trademark[] = TRADEMARK_Foundation; Index: projects/clang380-import/sys/sys/param.h =================================================================== --- projects/clang380-import/sys/sys/param.h (revision 293015) +++ projects/clang380-import/sys/sys/param.h (revision 293016) @@ -1,363 +1,363 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)param.h 8.3 (Berkeley) 4/4/95 * $FreeBSD$ */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here in the doc/ repository: * * head/en_US.ISO8859-1/books/porters-handbook/versions/chapter.xml * * scheme is: Rxx * 'R' is in the range 0 to 4 if this is a release branch or * x.0-CURRENT before RELENG_*_0 is created, otherwise 'R' is * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1100092 /* Master, propagated to newvers */ +#define __FreeBSD_version 1100093 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, * which by definition is always true on FreeBSD. This macro is also defined * on other systems that use the kernel of FreeBSD, such as GNU/kFreeBSD. * * It is tempting to use this macro in userland code when we want to enable * kernel-specific routines, and in fact it's fine to do this in code that * is part of FreeBSD itself. However, be aware that as presence of this * macro is still not widespread (e.g. older FreeBSD versions, 3rd party * compilers, etc), it is STRONGLY DISCOURAGED to check for this macro in * external applications without also checking for __FreeBSD__ as an * alternative. */ #undef __FreeBSD_kernel__ #define __FreeBSD_kernel__ #ifdef _KERNEL #define P_OSREL_SIGWAIT 700000 #define P_OSREL_SIGSEGV 700004 #define P_OSREL_MAP_ANON 800104 #define P_OSREL_MAP_FSTRICT 1100036 #define P_OSREL_SHUTDOWN_ENOTCONN 1100077 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP PATH_MAX /* max interpreter file name length */ #define MAXLOGNAME 33 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS (NGROUPS_MAX+1) /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 63 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif #ifndef _KERNEL /* Signals. */ #include #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * MAXBCACHEBUF - Maximum size of a buffer in the buffer cache. This must * be >= MAXBSIZE and can be set differently for different * architectures by defining it in . * Making this larger allows NFS to do larger reads/writes. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * The default value here can be overridden on a per-architecture * basis by defining it in . This should * probably be done to increase its value, when MAXBCACHEBUF is * defined as a larger value in . * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #ifndef MAXBCACHEBUF #define MAXBCACHEBUF MAXBSIZE /* must be a power of 2 >= MAXBSIZE */ #endif #ifndef BKVASIZE #define BKVASIZE 16384 /* must be power of 2 */ #endif #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #define rounddown(x, y) (((x)/(y))*(y)) #define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef lint #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* lint */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that * can be calculated (assuming 32 bits) can be closely approximated using * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15). * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) /* * Old spelling of __containerof(). */ #define member2struct(s, m, x) \ ((struct s *)(void *)((char *)(x) - offsetof(struct s, m))) /* * Access a variable length array that has been declared as a fixed * length array. */ #define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset]) #endif /* _SYS_PARAM_H_ */ Index: projects/clang380-import/sys/xen/xenbus/xenbusb.c =================================================================== --- projects/clang380-import/sys/xen/xenbus/xenbusb.c (revision 293015) +++ projects/clang380-import/sys/xen/xenbus/xenbusb.c (revision 293016) @@ -1,972 +1,970 @@ /****************************************************************************** * Copyright (C) 2010 Spectra Logic Corporation * Copyright (C) 2008 Doug Rabson * Copyright (C) 2005 Rusty Russell, IBM Corporation * Copyright (C) 2005 Mike Wray, Hewlett-Packard * Copyright (C) 2005 XenSource Ltd * * This file may be distributed separately from the Linux kernel, or * incorporated into other software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /** * \file xenbusb.c * * \brief Shared support functions for managing the NewBus busses that contain * Xen front and back end device instances. * * The NewBus implementation of XenBus attaches a xenbusb_front and xenbusb_back * child bus to the xenstore device. This strategy allows the small differences * in the handling of XenBus operations for front and back devices to be handled * as overrides in xenbusb_front/back.c. Front and back specific device * classes are also provided so device drivers can register for the devices they * can handle without the need to filter within their probe routines. The * net result is a device hierarchy that might look like this: * * xenstore0/ * xenbusb_front0/ * xn0 * xbd0 * xbd1 * xenbusb_back0/ * xbbd0 * xnb0 * xnb1 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*------------------------- Private Functions --------------------------------*/ /** * \brief Deallocate XenBus device instance variables. * * \param ivars The instance variable block to free. */ static void xenbusb_free_child_ivars(struct xenbus_device_ivars *ivars) { if (ivars->xd_otherend_watch.node != NULL) { xs_unregister_watch(&ivars->xd_otherend_watch); free(ivars->xd_otherend_watch.node, M_XENBUS); ivars->xd_otherend_watch.node = NULL; } if (ivars->xd_local_watch.node != NULL) { xs_unregister_watch(&ivars->xd_local_watch); ivars->xd_local_watch.node = NULL; } if (ivars->xd_node != NULL) { free(ivars->xd_node, M_XENBUS); ivars->xd_node = NULL; } ivars->xd_node_len = 0; if (ivars->xd_type != NULL) { free(ivars->xd_type, M_XENBUS); ivars->xd_type = NULL; } if (ivars->xd_otherend_path != NULL) { free(ivars->xd_otherend_path, M_XENBUS); ivars->xd_otherend_path = NULL; } ivars->xd_otherend_path_len = 0; free(ivars, M_XENBUS); } /** * XenBus watch callback registered against the "state" XenStore * node of the other-end of a split device connection. * * This callback is invoked whenever the state of a device instance's * peer changes. * * \param watch The xs_watch object used to register this callback * function. * \param vec An array of pointers to NUL terminated strings containing * watch event data. The vector should be indexed via the * xs_watch_type enum in xs_wire.h. * \param vec_size The number of elements in vec. */ static void xenbusb_otherend_watch_cb(struct xs_watch *watch, const char **vec, unsigned int vec_size __unused) { struct xenbus_device_ivars *ivars; device_t child; device_t bus; const char *path; enum xenbus_state newstate; ivars = (struct xenbus_device_ivars *)watch->callback_data; child = ivars->xd_dev; bus = device_get_parent(child); path = vec[XS_WATCH_PATH]; if (ivars->xd_otherend_path == NULL || strncmp(ivars->xd_otherend_path, path, ivars->xd_otherend_path_len)) return; newstate = xenbus_read_driver_state(ivars->xd_otherend_path); XENBUSB_OTHEREND_CHANGED(bus, child, newstate); } /** * XenBus watch callback registered against the XenStore sub-tree * represnting the local half of a split device connection. * * This callback is invoked whenever any XenStore data in the subtree * is modified, either by us or another privledged domain. * * \param watch The xs_watch object used to register this callback * function. * \param vec An array of pointers to NUL terminated strings containing * watch event data. The vector should be indexed via the * xs_watch_type enum in xs_wire.h. * \param vec_size The number of elements in vec. * */ static void xenbusb_local_watch_cb(struct xs_watch *watch, const char **vec, unsigned int vec_size __unused) { struct xenbus_device_ivars *ivars; device_t child; device_t bus; const char *path; ivars = (struct xenbus_device_ivars *)watch->callback_data; child = ivars->xd_dev; bus = device_get_parent(child); path = vec[XS_WATCH_PATH]; if (ivars->xd_node == NULL || strncmp(ivars->xd_node, path, ivars->xd_node_len)) return; XENBUSB_LOCALEND_CHANGED(bus, child, &path[ivars->xd_node_len]); } /** * Search our internal record of configured devices (not the XenStore) * to determine if the XenBus device indicated by \a node is known to * the system. * * \param dev The XenBus bus instance to search for device children. * \param node The XenStore node path for the device to find. * * \return The device_t of the found device if any, or NULL. * * \note device_t is a pointer type, so it can be compared against * NULL for validity. */ static device_t xenbusb_device_exists(device_t dev, const char *node) { device_t *kids; device_t result; struct xenbus_device_ivars *ivars; int i, count; if (device_get_children(dev, &kids, &count)) return (FALSE); result = NULL; for (i = 0; i < count; i++) { ivars = device_get_ivars(kids[i]); if (!strcmp(ivars->xd_node, node)) { result = kids[i]; break; } } free(kids, M_TEMP); return (result); } static void xenbusb_delete_child(device_t dev, device_t child) { struct xenbus_device_ivars *ivars; ivars = device_get_ivars(child); /* * We no longer care about the otherend of the * connection. Cancel the watches now so that we * don't try to handle an event for a partially * detached child. */ if (ivars->xd_otherend_watch.node != NULL) xs_unregister_watch(&ivars->xd_otherend_watch); if (ivars->xd_local_watch.node != NULL) xs_unregister_watch(&ivars->xd_local_watch); device_delete_child(dev, child); xenbusb_free_child_ivars(ivars); } /** * \param dev The NewBus device representing this XenBus bus. * \param child The NewBus device representing a child of dev%'s XenBus bus. */ static void xenbusb_verify_device(device_t dev, device_t child) { if (xs_exists(XST_NIL, xenbus_get_node(child), "") == 0) { /* * Device tree has been removed from Xenbus. * Tear down the device. */ xenbusb_delete_child(dev, child); } } /** * \brief Enumerate the devices on a XenBus bus and register them with * the NewBus device tree. * * xenbusb_enumerate_bus() will create entries (in state DS_NOTPRESENT) * for nodes that appear in the XenStore, but will not invoke probe/attach * operations on drivers. Probe/Attach processing must be separately * performed via an invocation of xenbusb_probe_children(). This is usually * done via the xbs_probe_children task. * * \param xbs XenBus Bus device softc of the owner of the bus to enumerate. * * \return On success, 0. Otherwise an errno value indicating the * type of failure. */ static int xenbusb_enumerate_bus(struct xenbusb_softc *xbs) { const char **types; u_int type_idx; u_int type_count; int error; error = xs_directory(XST_NIL, xbs->xbs_node, "", &type_count, &types); if (error) return (error); for (type_idx = 0; type_idx < type_count; type_idx++) XENBUSB_ENUMERATE_TYPE(xbs->xbs_dev, types[type_idx]); free(types, M_XENSTORE); return (0); } /** * Handler for all generic XenBus device systcl nodes. */ static int xenbusb_device_sysctl_handler(SYSCTL_HANDLER_ARGS) { device_t dev; const char *value; dev = (device_t)arg1; switch (arg2) { case XENBUS_IVAR_NODE: value = xenbus_get_node(dev); break; case XENBUS_IVAR_TYPE: value = xenbus_get_type(dev); break; case XENBUS_IVAR_STATE: value = xenbus_strstate(xenbus_get_state(dev)); break; case XENBUS_IVAR_OTHEREND_ID: return (sysctl_handle_int(oidp, NULL, xenbus_get_otherend_id(dev), req)); /* NOTREACHED */ case XENBUS_IVAR_OTHEREND_PATH: value = xenbus_get_otherend_path(dev); break; default: return (EINVAL); } return (SYSCTL_OUT_STR(req, value)); } /** * Create read-only systcl nodes for xenbusb device ivar data. * * \param dev The XenBus device instance to register with sysctl. */ static void xenbusb_device_sysctl_init(device_t dev) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "xenstore_path", CTLTYPE_STRING | CTLFLAG_RD, dev, XENBUS_IVAR_NODE, xenbusb_device_sysctl_handler, "A", "XenStore path to device"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "xenbus_dev_type", CTLTYPE_STRING | CTLFLAG_RD, dev, XENBUS_IVAR_TYPE, xenbusb_device_sysctl_handler, "A", "XenBus device type"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "xenbus_connection_state", CTLTYPE_STRING | CTLFLAG_RD, dev, XENBUS_IVAR_STATE, xenbusb_device_sysctl_handler, "A", "XenBus state of peer connection"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "xenbus_peer_domid", CTLTYPE_INT | CTLFLAG_RD, dev, XENBUS_IVAR_OTHEREND_ID, xenbusb_device_sysctl_handler, "I", "Xen domain ID of peer"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "xenstore_peer_path", CTLTYPE_STRING | CTLFLAG_RD, dev, XENBUS_IVAR_OTHEREND_PATH, xenbusb_device_sysctl_handler, "A", "XenStore path to peer device"); } /** * \brief Decrement the number of XenBus child devices in the * connecting state by one and release the xbs_attch_ch * interrupt configuration hook if the connecting count * drops to zero. * * \param xbs XenBus Bus device softc of the owner of the bus to enumerate. */ static void xenbusb_release_confighook(struct xenbusb_softc *xbs) { mtx_lock(&xbs->xbs_lock); KASSERT(xbs->xbs_connecting_children > 0, ("Connecting device count error\n")); xbs->xbs_connecting_children--; if (xbs->xbs_connecting_children == 0 && (xbs->xbs_flags & XBS_ATTACH_CH_ACTIVE) != 0) { xbs->xbs_flags &= ~XBS_ATTACH_CH_ACTIVE; mtx_unlock(&xbs->xbs_lock); config_intrhook_disestablish(&xbs->xbs_attach_ch); } else { mtx_unlock(&xbs->xbs_lock); } } /** * \brief Verify the existance of attached device instances and perform * probe/attach processing for newly arrived devices. * * \param dev The NewBus device representing this XenBus bus. * * \return On success, 0. Otherwise an errno value indicating the * type of failure. */ static int xenbusb_probe_children(device_t dev) { device_t *kids; struct xenbus_device_ivars *ivars; int i, count, error; if (device_get_children(dev, &kids, &count) == 0) { for (i = 0; i < count; i++) { if (device_get_state(kids[i]) != DS_NOTPRESENT) { /* * We already know about this one. * Make sure it's still here. */ xenbusb_verify_device(dev, kids[i]); continue; } error = device_probe_and_attach(kids[i]); if (error == ENXIO) { struct xenbusb_softc *xbs; /* * We don't have a PV driver for this device. * However, an emulated device we do support * may share this backend. Hide the node from * XenBus until the next rescan, but leave it's * state unchanged so we don't inadvertently * prevent attachment of any emulated device. */ xenbusb_delete_child(dev, kids[i]); /* * Since the XenStore state of this device * still indicates a pending attach, manually * release it's hold on the boot process. */ xbs = device_get_softc(dev); xenbusb_release_confighook(xbs); continue; } else if (error) { /* * Transition device to the closed state * so the world knows that attachment will * not occur. */ xenbus_set_state(kids[i], XenbusStateClosed); /* * Remove our record of this device. * So long as it remains in the closed * state in the XenStore, we will not find * it again. The state will only change * if the control domain actively reconfigures * this device. */ xenbusb_delete_child(dev, kids[i]); continue; } /* * Augment default newbus provided dynamic sysctl * variables with the standard ivar contents of * XenBus devices. */ xenbusb_device_sysctl_init(kids[i]); /* * Now that we have a driver managing this device * that can receive otherend state change events, * hook up a watch for them. */ ivars = device_get_ivars(kids[i]); xs_register_watch(&ivars->xd_otherend_watch); xs_register_watch(&ivars->xd_local_watch); } free(kids, M_TEMP); } return (0); } /** * \brief Task callback function to perform XenBus probe operations * from a known safe context. * * \param arg The NewBus device_t representing the bus instance to * on which to perform probe processing. * \param pending The number of times this task was queued before it could * be run. */ static void xenbusb_probe_children_cb(void *arg, int pending __unused) { device_t dev = (device_t)arg; /* * Hold Giant until the Giant free newbus changes are committed. */ mtx_lock(&Giant); xenbusb_probe_children(dev); mtx_unlock(&Giant); } /** * \brief XenStore watch callback for the root node of the XenStore * subtree representing a XenBus. * * This callback performs, or delegates to the xbs_probe_children task, * all processing necessary to handle dynmaic device arrival and departure * events from a XenBus. * * \param watch The XenStore watch object associated with this callback. * \param vec The XenStore watch event data. * \param len The number of fields in the event data stream. */ static void xenbusb_devices_changed(struct xs_watch *watch, const char **vec, unsigned int len) { struct xenbusb_softc *xbs; device_t dev; char *node; - char *bus; char *type; char *id; char *p; u_int component; xbs = (struct xenbusb_softc *)watch->callback_data; dev = xbs->xbs_dev; if (len <= XS_WATCH_PATH) { device_printf(dev, "xenbusb_devices_changed: " "Short Event Data.\n"); return; } node = strdup(vec[XS_WATCH_PATH], M_XENBUS); p = strchr(node, '/'); if (p == NULL) goto out; - bus = node; *p = 0; type = p + 1; p = strchr(type, '/'); if (p == NULL) goto out; *p++ = 0; /* * Extract the device ID. A device ID has one or more path * components separated by the '/' character. * * e.g. "/" for backend devices. */ id = p; for (component = 0; component < xbs->xbs_id_components; component++) { p = strchr(p, '/'); if (p == NULL) break; p++; } if (p != NULL) *p = 0; if (*id != 0 && component >= xbs->xbs_id_components - 1) { xenbusb_add_device(xbs->xbs_dev, type, id); taskqueue_enqueue(taskqueue_thread, &xbs->xbs_probe_children); } out: free(node, M_XENBUS); } /** * \brief Interrupt configuration hook callback associated with xbs_attch_ch. * * Since interrupts are always functional at the time of XenBus configuration, * there is nothing to be done when the callback occurs. This hook is only * registered to hold up boot processing while XenBus devices come online. * * \param arg Unused configuration hook callback argument. */ static void xenbusb_nop_confighook_cb(void *arg __unused) { } /*--------------------------- Public Functions -------------------------------*/ /*--------- API comments for these methods can be found in xenbusb.h ---------*/ void xenbusb_identify(driver_t *driver __unused, device_t parent) { /* * A single instance of each bus type for which we have a driver * is always present in a system operating under Xen. */ BUS_ADD_CHILD(parent, 0, driver->name, 0); } int xenbusb_add_device(device_t dev, const char *type, const char *id) { struct xenbusb_softc *xbs; struct sbuf *devpath_sbuf; char *devpath; struct xenbus_device_ivars *ivars; int error; xbs = device_get_softc(dev); devpath_sbuf = sbuf_new_auto(); sbuf_printf(devpath_sbuf, "%s/%s/%s", xbs->xbs_node, type, id); sbuf_finish(devpath_sbuf); devpath = sbuf_data(devpath_sbuf); ivars = malloc(sizeof(*ivars), M_XENBUS, M_ZERO|M_WAITOK); error = ENXIO; if (xs_exists(XST_NIL, devpath, "") != 0) { device_t child; enum xenbus_state state; char *statepath; child = xenbusb_device_exists(dev, devpath); if (child != NULL) { /* * We are already tracking this node */ error = 0; goto out; } state = xenbus_read_driver_state(devpath); if (state != XenbusStateInitialising) { /* * Device is not new, so ignore it. This can * happen if a device is going away after * switching to Closed. */ printf("xenbusb_add_device: Device %s ignored. " "State %d\n", devpath, state); error = 0; goto out; } sx_init(&ivars->xd_lock, "xdlock"); ivars->xd_flags = XDF_CONNECTING; ivars->xd_node = strdup(devpath, M_XENBUS); ivars->xd_node_len = strlen(devpath); ivars->xd_type = strdup(type, M_XENBUS); ivars->xd_state = XenbusStateInitialising; error = XENBUSB_GET_OTHEREND_NODE(dev, ivars); if (error) { printf("xenbus_update_device: %s no otherend id\n", devpath); goto out; } statepath = malloc(ivars->xd_otherend_path_len + strlen("/state") + 1, M_XENBUS, M_WAITOK); sprintf(statepath, "%s/state", ivars->xd_otherend_path); ivars->xd_otherend_watch.node = statepath; ivars->xd_otherend_watch.callback = xenbusb_otherend_watch_cb; ivars->xd_otherend_watch.callback_data = (uintptr_t)ivars; ivars->xd_local_watch.node = ivars->xd_node; ivars->xd_local_watch.callback = xenbusb_local_watch_cb; ivars->xd_local_watch.callback_data = (uintptr_t)ivars; mtx_lock(&xbs->xbs_lock); xbs->xbs_connecting_children++; mtx_unlock(&xbs->xbs_lock); child = device_add_child(dev, NULL, -1); ivars->xd_dev = child; device_set_ivars(child, ivars); } out: sbuf_delete(devpath_sbuf); if (error != 0) xenbusb_free_child_ivars(ivars); return (error); } int xenbusb_attach(device_t dev, char *bus_node, u_int id_components) { struct xenbusb_softc *xbs; xbs = device_get_softc(dev); mtx_init(&xbs->xbs_lock, "xenbusb softc lock", NULL, MTX_DEF); xbs->xbs_node = bus_node; xbs->xbs_id_components = id_components; xbs->xbs_dev = dev; /* * Since XenBus busses are attached to the XenStore, and * the XenStore does not probe children until after interrupt * services are available, this config hook is used solely * to ensure that the remainder of the boot process (e.g. * mount root) is deferred until child devices are adequately * probed. We unblock the boot process as soon as the * connecting child count in our softc goes to 0. */ xbs->xbs_attach_ch.ich_func = xenbusb_nop_confighook_cb; xbs->xbs_attach_ch.ich_arg = dev; config_intrhook_establish(&xbs->xbs_attach_ch); xbs->xbs_flags |= XBS_ATTACH_CH_ACTIVE; xbs->xbs_connecting_children = 1; /* * The subtree for this bus type may not yet exist * causing initial enumeration to fail. We still * want to return success from our attach though * so that we are ready to handle devices for this * bus when they are dynamically attached to us * by a Xen management action. */ (void)xenbusb_enumerate_bus(xbs); xenbusb_probe_children(dev); xbs->xbs_device_watch.node = bus_node; xbs->xbs_device_watch.callback = xenbusb_devices_changed; xbs->xbs_device_watch.callback_data = (uintptr_t)xbs; TASK_INIT(&xbs->xbs_probe_children, 0, xenbusb_probe_children_cb, dev); xs_register_watch(&xbs->xbs_device_watch); xenbusb_release_confighook(xbs); return (0); } int xenbusb_resume(device_t dev) { device_t *kids; struct xenbus_device_ivars *ivars; int i, count, error; char *statepath; /* * We must re-examine each device and find the new path for * its backend. */ if (device_get_children(dev, &kids, &count) == 0) { for (i = 0; i < count; i++) { if (device_get_state(kids[i]) == DS_NOTPRESENT) continue; ivars = device_get_ivars(kids[i]); xs_unregister_watch(&ivars->xd_otherend_watch); xenbus_set_state(kids[i], XenbusStateInitialising); /* * Find the new backend details and * re-register our watch. */ error = XENBUSB_GET_OTHEREND_NODE(dev, ivars); if (error) return (error); statepath = malloc(ivars->xd_otherend_path_len + strlen("/state") + 1, M_XENBUS, M_WAITOK); sprintf(statepath, "%s/state", ivars->xd_otherend_path); free(ivars->xd_otherend_watch.node, M_XENBUS); ivars->xd_otherend_watch.node = statepath; DEVICE_RESUME(kids[i]); xs_register_watch(&ivars->xd_otherend_watch); #if 0 /* * Can't do this yet since we are running in * the xenwatch thread and if we sleep here, * we will stop delivering watch notifications * and the device will never come back online. */ sx_xlock(&ivars->xd_lock); while (ivars->xd_state != XenbusStateClosed && ivars->xd_state != XenbusStateConnected) sx_sleep(&ivars->xd_state, &ivars->xd_lock, 0, "xdresume", 0); sx_xunlock(&ivars->xd_lock); #endif } free(kids, M_TEMP); } return (0); } int xenbusb_print_child(device_t dev, device_t child) { struct xenbus_device_ivars *ivars = device_get_ivars(child); int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(" at %s", ivars->xd_node); retval += bus_print_child_footer(dev, child); return (retval); } int xenbusb_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { struct xenbus_device_ivars *ivars = device_get_ivars(child); switch (index) { case XENBUS_IVAR_NODE: *result = (uintptr_t) ivars->xd_node; return (0); case XENBUS_IVAR_TYPE: *result = (uintptr_t) ivars->xd_type; return (0); case XENBUS_IVAR_STATE: *result = (uintptr_t) ivars->xd_state; return (0); case XENBUS_IVAR_OTHEREND_ID: *result = (uintptr_t) ivars->xd_otherend_id; return (0); case XENBUS_IVAR_OTHEREND_PATH: *result = (uintptr_t) ivars->xd_otherend_path; return (0); } return (ENOENT); } int xenbusb_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { struct xenbus_device_ivars *ivars = device_get_ivars(child); enum xenbus_state newstate; int currstate; switch (index) { case XENBUS_IVAR_STATE: { int error; newstate = (enum xenbus_state)value; sx_xlock(&ivars->xd_lock); if (ivars->xd_state == newstate) { error = 0; goto out; } error = xs_scanf(XST_NIL, ivars->xd_node, "state", NULL, "%d", &currstate); if (error) goto out; do { error = xs_printf(XST_NIL, ivars->xd_node, "state", "%d", newstate); } while (error == EAGAIN); if (error) { /* * Avoid looping through xenbus_dev_fatal() * which calls xenbus_write_ivar to set the * state to closing. */ if (newstate != XenbusStateClosing) xenbus_dev_fatal(dev, error, "writing new state"); goto out; } ivars->xd_state = newstate; if ((ivars->xd_flags & XDF_CONNECTING) != 0 && (newstate == XenbusStateClosed || newstate == XenbusStateConnected)) { struct xenbusb_softc *xbs; ivars->xd_flags &= ~XDF_CONNECTING; xbs = device_get_softc(dev); xenbusb_release_confighook(xbs); } wakeup(&ivars->xd_state); out: sx_xunlock(&ivars->xd_lock); return (error); } case XENBUS_IVAR_NODE: case XENBUS_IVAR_TYPE: case XENBUS_IVAR_OTHEREND_ID: case XENBUS_IVAR_OTHEREND_PATH: /* * These variables are read-only. */ return (EINVAL); } return (ENOENT); } void xenbusb_otherend_changed(device_t bus, device_t child, enum xenbus_state state) { XENBUS_OTHEREND_CHANGED(child, state); } void xenbusb_localend_changed(device_t bus, device_t child, const char *path) { if (strcmp(path, "/state") != 0) { struct xenbus_device_ivars *ivars; ivars = device_get_ivars(child); sx_xlock(&ivars->xd_lock); ivars->xd_state = xenbus_read_driver_state(ivars->xd_node); sx_xunlock(&ivars->xd_lock); } XENBUS_LOCALEND_CHANGED(child, path); } Index: projects/clang380-import/sys =================================================================== --- projects/clang380-import/sys (revision 293015) +++ projects/clang380-import/sys (revision 293016) Property changes on: projects/clang380-import/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r292951-293015 Index: projects/clang380-import/tests/sys/kern/unix_passfd_test.c =================================================================== --- projects/clang380-import/tests/sys/kern/unix_passfd_test.c (revision 293015) +++ projects/clang380-import/tests/sys/kern/unix_passfd_test.c (revision 293016) @@ -1,396 +1,396 @@ /*- * Copyright (c) 2005 Robert N. M. Watson * Copyright (c) 2015 Mark Johnston * 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$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * UNIX domain sockets allow file descriptors to be passed via "ancillary * data", or control messages. This regression test is intended to exercise * this facility, both performing some basic tests that it operates, and also * causing some kernel edge cases to execute, such as garbage collection when * there are cyclic file descriptor references. Right now we test only with * stream sockets, but ideally we'd also test with datagram sockets. */ static void domainsocketpair(int *fdp) { ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, fdp) != -1, "socketpair(PF_UNIX, SOCK_STREAM) failed: %s", strerror(errno)); } static void closesocketpair(int *fdp) { close(fdp[0]); close(fdp[1]); } static void devnull(int *fdp) { int fd; fd = open("/dev/null", O_RDONLY); ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); *fdp = fd; } static void tempfile(int *fdp) { char path[PATH_MAX]; int fd; snprintf(path, PATH_MAX, "%s/unix_passfd.XXXXXXXXXXXXXXX", getenv("TMPDIR") == NULL ? "/tmp" : getenv("TMPDIR")); fd = mkstemp(path); ATF_REQUIRE_MSG(fd != -1, "mkstemp(%s) failed", path); (void)unlink(path); *fdp = fd; } static void dofstat(int fd, struct stat *sb) { ATF_REQUIRE_MSG(fstat(fd, sb) == 0, "fstat failed: %s", strerror(errno)); } static void samefile(struct stat *sb1, struct stat *sb2) { ATF_REQUIRE_MSG(sb1->st_dev == sb2->st_dev, "different device"); ATF_REQUIRE_MSG(sb1->st_ino == sb2->st_ino, "different inode"); } static void -sendfd_payload(int sockfd, int sendfd, void *payload, size_t paylen) +sendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen) { struct iovec iovec; char message[CMSG_SPACE(sizeof(int))]; struct cmsghdr *cmsghdr; struct msghdr msghdr; ssize_t len; bzero(&msghdr, sizeof(msghdr)); bzero(&message, sizeof(message)); msghdr.msg_control = message; msghdr.msg_controllen = sizeof(message); iovec.iov_base = payload; iovec.iov_len = paylen; msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; cmsghdr = (struct cmsghdr *)(void*)message; cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); cmsghdr->cmsg_level = SOL_SOCKET; cmsghdr->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsghdr), &sendfd, sizeof(int)); + memcpy(CMSG_DATA(cmsghdr), &send_fd, sizeof(int)); len = sendmsg(sockfd, &msghdr, 0); ATF_REQUIRE_MSG(len != -1, "sendmsg failed: %s", strerror(errno)); ATF_REQUIRE_MSG((size_t)len == paylen, "sendmsg: %zd messages sent; expected: %zu; %s", len, paylen, strerror(errno)); } static void -sendfd(int sockfd, int sendfd) +sendfd(int sockfd, int send_fd) { char ch = 0; - return (sendfd_payload(sockfd, sendfd, &ch, sizeof(ch))); + return (sendfd_payload(sockfd, send_fd, &ch, sizeof(ch))); } static void -recvfd_payload(int sockfd, int *recvfd, void *buf, size_t buflen) +recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen) { struct cmsghdr *cmsghdr; char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + sizeof(int)]; struct msghdr msghdr; struct iovec iovec; ssize_t len; bzero(&msghdr, sizeof(msghdr)); msghdr.msg_control = message; msghdr.msg_controllen = sizeof(message); iovec.iov_base = buf; iovec.iov_len = buflen; msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; len = recvmsg(sockfd, &msghdr, 0); ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno)); ATF_REQUIRE_MSG((size_t)len == buflen, "recvmsg: %zd bytes received; expected %zd", len, buflen); cmsghdr = CMSG_FIRSTHDR(&msghdr); ATF_REQUIRE_MSG(cmsghdr != NULL, "recvmsg: did not receive control message"); - *recvfd = -1; + *recv_fd = -1; for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { if (cmsghdr->cmsg_level == SOL_SOCKET && cmsghdr->cmsg_type == SCM_RIGHTS && cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) { - memcpy(recvfd, CMSG_DATA(cmsghdr), sizeof(int)); - ATF_REQUIRE(*recvfd != -1); + memcpy(recv_fd, CMSG_DATA(cmsghdr), sizeof(int)); + ATF_REQUIRE(*recv_fd != -1); } } - ATF_REQUIRE_MSG(*recvfd != -1, + ATF_REQUIRE_MSG(*recv_fd != -1, "recvmsg: did not receive single-fd message"); } static void -recvfd(int sockfd, int *recvfd) +recvfd(int sockfd, int *recv_fd) { char ch = 0; - return (recvfd_payload(sockfd, recvfd, &ch, sizeof(ch))); + return (recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch))); } /* * Put a temporary file into a UNIX domain socket, then take it out and make * sure it's the same file. First time around, don't close the reference * after sending. */ ATF_TC_WITHOUT_HEAD(simple_send_fd); ATF_TC_BODY(simple_send_fd, tc) { struct stat getfd_stat, putfd_stat; int fd[2], getfd, putfd; domainsocketpair(fd); tempfile(&putfd); dofstat(putfd, &putfd_stat); sendfd(fd[0], putfd); recvfd(fd[1], &getfd); dofstat(getfd, &getfd_stat); samefile(&putfd_stat, &getfd_stat); close(putfd); close(getfd); closesocketpair(fd); } /* * Same as simple_send_fd, only close the file reference after sending, so that * the only reference is the descriptor in the UNIX domain socket buffer. */ ATF_TC_WITHOUT_HEAD(send_and_close); ATF_TC_BODY(send_and_close, tc) { struct stat getfd_stat, putfd_stat; int fd[2], getfd, putfd; domainsocketpair(fd); tempfile(&putfd); dofstat(putfd, &putfd_stat); sendfd(fd[0], putfd); close(putfd); recvfd(fd[1], &getfd); dofstat(getfd, &getfd_stat); samefile(&putfd_stat, &getfd_stat); close(getfd); closesocketpair(fd); } /* * Put a temporary file into a UNIX domain socket, then close both endpoints * causing garbage collection to kick off. */ ATF_TC_WITHOUT_HEAD(send_and_cancel); ATF_TC_BODY(send_and_cancel, tc) { int fd[2], putfd; domainsocketpair(fd); tempfile(&putfd); sendfd(fd[0], putfd); close(putfd); closesocketpair(fd); } /* * Send two files. Then receive them. Make sure they are returned in the * right order, and both get there. */ ATF_TC_WITHOUT_HEAD(two_files); ATF_TC_BODY(two_files, tc) { struct stat getfd_1_stat, getfd_2_stat, putfd_1_stat, putfd_2_stat; int fd[2], getfd_1, getfd_2, putfd_1, putfd_2; domainsocketpair(fd); tempfile(&putfd_1); tempfile(&putfd_2); dofstat(putfd_1, &putfd_1_stat); dofstat(putfd_2, &putfd_2_stat); sendfd(fd[0], putfd_1); sendfd(fd[0], putfd_2); close(putfd_1); close(putfd_2); recvfd(fd[1], &getfd_1); recvfd(fd[1], &getfd_2); dofstat(getfd_1, &getfd_1_stat); dofstat(getfd_2, &getfd_2_stat); samefile(&putfd_1_stat, &getfd_1_stat); samefile(&putfd_2_stat, &getfd_2_stat); close(getfd_1); close(getfd_2); closesocketpair(fd); } /* * Big bundling test. Send an endpoint of the UNIX domain socket over itself, * closing the door behind it. */ ATF_TC_WITHOUT_HEAD(bundle); ATF_TC_BODY(bundle, tc) { int fd[2], getfd; domainsocketpair(fd); sendfd(fd[0], fd[0]); close(fd[0]); recvfd(fd[1], &getfd); close(getfd); close(fd[1]); } /* * Big bundling test part two: Send an endpoint of the UNIX domain socket over * itself, close the door behind it, and never remove it from the other end. */ ATF_TC_WITHOUT_HEAD(bundle_cancel); ATF_TC_BODY(bundle_cancel, tc) { int fd[2]; domainsocketpair(fd); sendfd(fd[0], fd[0]); sendfd(fd[1], fd[0]); closesocketpair(fd); } /* * Test for PR 151758: Send an character device over the UNIX domain socket * and then close both sockets to orphan the device. */ ATF_TC_WITHOUT_HEAD(devfs_orphan); ATF_TC_BODY(devfs_orphan, tc) { int fd[2], putfd; domainsocketpair(fd); devnull(&putfd); sendfd(fd[0], putfd); close(putfd); closesocketpair(fd); } #define LOCAL_SENDSPACE_SYSCTL "net.local.stream.sendspace" /* * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel prepends a * control message to the data. Sender sends large payload. * Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer limit, and receiver * receives truncated data. */ ATF_TC_WITHOUT_HEAD(rights_creds_payload); ATF_TC_BODY(rights_creds_payload, tc) { const int on = 1; u_long sendspace; size_t len; void *buf; int fd[2], getfd, putfd, rc; atf_tc_expect_fail("PR 181741: Packet loss when 'control' messages " "are present with large data"); len = sizeof(sendspace); rc = sysctlbyname(LOCAL_SENDSPACE_SYSCTL, &sendspace, &len, NULL, 0); ATF_REQUIRE_MSG(rc != -1, "sysctl %s failed: %s", LOCAL_SENDSPACE_SYSCTL, strerror(errno)); buf = calloc(1, sendspace); ATF_REQUIRE(buf != NULL); domainsocketpair(fd); rc = setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on)); ATF_REQUIRE_MSG(rc != -1, "setsockopt(LOCAL_CREDS) failed: %s", strerror(errno)); tempfile(&putfd); sendfd_payload(fd[0], putfd, buf, sendspace); recvfd_payload(fd[1], &getfd, buf, sendspace); close(putfd); close(getfd); closesocketpair(fd); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, simple_send_fd); ATF_TP_ADD_TC(tp, send_and_close); ATF_TP_ADD_TC(tp, send_and_cancel); ATF_TP_ADD_TC(tp, two_files); ATF_TP_ADD_TC(tp, bundle); ATF_TP_ADD_TC(tp, bundle_cancel); ATF_TP_ADD_TC(tp, devfs_orphan); ATF_TP_ADD_TC(tp, rights_creds_payload); return (atf_no_error()); } Index: projects/clang380-import/usr.sbin/bhyve/bhyverun.c =================================================================== --- projects/clang380-import/usr.sbin/bhyve/bhyverun.c (revision 293015) +++ projects/clang380-import/usr.sbin/bhyve/bhyverun.c (revision 293016) @@ -1,974 +1,971 @@ /*- * Copyright (c) 2011 NetApp, 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. * 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 NETAPP, INC ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC 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$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bhyverun.h" #include "acpi.h" #include "inout.h" #include "dbgport.h" #include "fwctl.h" #include "ioapic.h" #include "mem.h" #include "mevent.h" #include "mptbl.h" #include "pci_emul.h" #include "pci_irq.h" #include "pci_lpc.h" #include "smbiostbl.h" #include "xmsr.h" #include "spinup_ap.h" #include "rtc.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ #define MB (1024UL * 1024) #define GB (1024UL * MB) typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); extern int vmexit_task_switch(struct vmctx *, struct vm_exit *, int *vcpu); char *vmname; int guest_ncpus; char *guest_uuid_str; static int guest_vmexit_on_hlt, guest_vmexit_on_pause; static int virtio_msix = 1; static int x2apic_mode = 0; /* default is xAPIC */ static int strictio; static int strictmsr = 1; static int acpi; static char *progname; static const int BSP = 0; static cpuset_t cpumask; static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); static struct vm_exit vmexit[VM_MAXCPU]; struct bhyvestats { uint64_t vmexit_bogus; uint64_t vmexit_reqidle; uint64_t vmexit_hlt; uint64_t vmexit_pause; uint64_t vmexit_mtrap; uint64_t vmexit_inst_emul; uint64_t cpu_switch_rotate; uint64_t cpu_switch_direct; } stats; struct mt_vmm_info { pthread_t mt_thr; struct vmctx *mt_ctx; int mt_vcpu; } mt_vmm_info[VM_MAXCPU]; static cpuset_t *vcpumap[VM_MAXCPU] = { NULL }; static void usage(int code) { fprintf(stderr, "Usage: %s [-abehuwxACHPSWY] [-c vcpus] [-g ] [-l ]\n" " %*s [-m mem] [-p vcpu:hostcpu] [-s ] [-U uuid] \n" " -a: local apic is in xAPIC mode (deprecated)\n" " -A: create ACPI tables\n" " -c: # cpus (default 1)\n" " -C: include guest memory in core file\n" " -e: exit on unhandled I/O access\n" " -g: gdb port\n" " -h: help\n" " -H: vmexit from the guest on hlt\n" " -l: LPC device configuration\n" " -m: memory size in MB\n" " -p: pin 'vcpu' to 'hostcpu'\n" " -P: vmexit from the guest on pause\n" " -s: PCI slot config\n" " -S: guest memory cannot be swapped\n" " -u: RTC keeps UTC time\n" " -U: uuid\n" " -w: ignore unimplemented MSRs\n" " -W: force virtio to use single-vector MSI\n" " -x: local apic is in x2APIC mode\n" " -Y: disable MPtable generation\n", progname, (int)strlen(progname), ""); exit(code); } static int pincpu_parse(const char *opt) { int vcpu, pcpu; if (sscanf(opt, "%d:%d", &vcpu, &pcpu) != 2) { fprintf(stderr, "invalid format: %s\n", opt); return (-1); } if (vcpu < 0 || vcpu >= VM_MAXCPU) { fprintf(stderr, "vcpu '%d' outside valid range from 0 to %d\n", vcpu, VM_MAXCPU - 1); return (-1); } if (pcpu < 0 || pcpu >= CPU_SETSIZE) { fprintf(stderr, "hostcpu '%d' outside valid range from " "0 to %d\n", pcpu, CPU_SETSIZE - 1); return (-1); } if (vcpumap[vcpu] == NULL) { if ((vcpumap[vcpu] = malloc(sizeof(cpuset_t))) == NULL) { perror("malloc"); return (-1); } CPU_ZERO(vcpumap[vcpu]); } CPU_SET(pcpu, vcpumap[vcpu]); return (0); } void vm_inject_fault(void *arg, int vcpu, int vector, int errcode_valid, int errcode) { struct vmctx *ctx; int error, restart_instruction; ctx = arg; restart_instruction = 1; error = vm_inject_exception(ctx, vcpu, vector, errcode_valid, errcode, restart_instruction); assert(error == 0); } void * paddr_guest2host(struct vmctx *ctx, uintptr_t gaddr, size_t len) { return (vm_map_gpa(ctx, gaddr, len)); } int fbsdrun_vmexit_on_pause(void) { return (guest_vmexit_on_pause); } int fbsdrun_vmexit_on_hlt(void) { return (guest_vmexit_on_hlt); } int fbsdrun_virtio_msix(void) { return (virtio_msix); } static void * fbsdrun_start_thread(void *param) { char tname[MAXCOMLEN + 1]; struct mt_vmm_info *mtp; int vcpu; mtp = param; vcpu = mtp->mt_vcpu; snprintf(tname, sizeof(tname), "vcpu %d", vcpu); pthread_set_name_np(mtp->mt_thr, tname); vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip); /* not reached */ exit(1); return (NULL); } void fbsdrun_addcpu(struct vmctx *ctx, int fromcpu, int newcpu, uint64_t rip) { int error; assert(fromcpu == BSP); /* * The 'newcpu' must be activated in the context of 'fromcpu'. If * vm_activate_cpu() is delayed until newcpu's pthread starts running * then vmm.ko is out-of-sync with bhyve and this can create a race * with vm_suspend(). */ error = vm_activate_cpu(ctx, newcpu); if (error != 0) err(EX_OSERR, "could not activate CPU %d", newcpu); CPU_SET_ATOMIC(newcpu, &cpumask); /* * Set up the vmexit struct to allow execution to start * at the given RIP */ vmexit[newcpu].rip = rip; vmexit[newcpu].inst_length = 0; mt_vmm_info[newcpu].mt_ctx = ctx; mt_vmm_info[newcpu].mt_vcpu = newcpu; error = pthread_create(&mt_vmm_info[newcpu].mt_thr, NULL, fbsdrun_start_thread, &mt_vmm_info[newcpu]); assert(error == 0); } static int fbsdrun_deletecpu(struct vmctx *ctx, int vcpu) { if (!CPU_ISSET(vcpu, &cpumask)) { fprintf(stderr, "Attempting to delete unknown cpu %d\n", vcpu); exit(1); } CPU_CLR_ATOMIC(vcpu, &cpumask); return (CPU_EMPTY(&cpumask)); } static int vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu, uint32_t eax) { #if BHYVE_DEBUG /* * put guest-driven debug here */ #endif return (VMEXIT_CONTINUE); } static int vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) { int error; - int bytes, port, in, out, string; + int bytes, port, in, out; int vcpu; vcpu = *pvcpu; port = vme->u.inout.port; bytes = vme->u.inout.bytes; - string = vme->u.inout.string; in = vme->u.inout.in; out = !in; /* Extra-special case of host notifications */ if (out && port == GUEST_NIO_PORT) { error = vmexit_handle_notify(ctx, vme, pvcpu, vme->u.inout.eax); return (error); } error = emulate_inout(ctx, vcpu, vme, strictio); if (error) { fprintf(stderr, "Unhandled %s%c 0x%04x at 0x%lx\n", in ? "in" : "out", bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port, vmexit->rip); return (VMEXIT_ABORT); } else { return (VMEXIT_CONTINUE); } } static int vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) { uint64_t val; uint32_t eax, edx; int error; val = 0; error = emulate_rdmsr(ctx, *pvcpu, vme->u.msr.code, &val); if (error != 0) { fprintf(stderr, "rdmsr to register %#x on vcpu %d\n", vme->u.msr.code, *pvcpu); if (strictmsr) { vm_inject_gp(ctx, *pvcpu); return (VMEXIT_CONTINUE); } } eax = val; error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RAX, eax); assert(error == 0); edx = val >> 32; error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RDX, edx); assert(error == 0); return (VMEXIT_CONTINUE); } static int vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) { int error; error = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code, vme->u.msr.wval); if (error != 0) { fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n", vme->u.msr.code, vme->u.msr.wval, *pvcpu); if (strictmsr) { vm_inject_gp(ctx, *pvcpu); return (VMEXIT_CONTINUE); } } return (VMEXIT_CONTINUE); } static int vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) { int newcpu; int retval = VMEXIT_CONTINUE; newcpu = spinup_ap(ctx, *pvcpu, vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip); return (retval); } #define DEBUG_EPT_MISCONFIG #ifdef DEBUG_EPT_MISCONFIG #define EXIT_REASON_EPT_MISCONFIG 49 #define VMCS_GUEST_PHYSICAL_ADDRESS 0x00002400 #define VMCS_IDENT(x) ((x) | 0x80000000) static uint64_t ept_misconfig_gpa, ept_misconfig_pte[4]; static int ept_misconfig_ptenum; #endif static int vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { fprintf(stderr, "vm exit[%d]\n", *pvcpu); fprintf(stderr, "\treason\t\tVMX\n"); fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip); fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length); fprintf(stderr, "\tstatus\t\t%d\n", vmexit->u.vmx.status); fprintf(stderr, "\texit_reason\t%u\n", vmexit->u.vmx.exit_reason); fprintf(stderr, "\tqualification\t0x%016lx\n", vmexit->u.vmx.exit_qualification); fprintf(stderr, "\tinst_type\t\t%d\n", vmexit->u.vmx.inst_type); fprintf(stderr, "\tinst_error\t\t%d\n", vmexit->u.vmx.inst_error); #ifdef DEBUG_EPT_MISCONFIG if (vmexit->u.vmx.exit_reason == EXIT_REASON_EPT_MISCONFIG) { vm_get_register(ctx, *pvcpu, VMCS_IDENT(VMCS_GUEST_PHYSICAL_ADDRESS), &ept_misconfig_gpa); vm_get_gpa_pmap(ctx, ept_misconfig_gpa, ept_misconfig_pte, &ept_misconfig_ptenum); fprintf(stderr, "\tEPT misconfiguration:\n"); fprintf(stderr, "\t\tGPA: %#lx\n", ept_misconfig_gpa); fprintf(stderr, "\t\tPTE(%d): %#lx %#lx %#lx %#lx\n", ept_misconfig_ptenum, ept_misconfig_pte[0], ept_misconfig_pte[1], ept_misconfig_pte[2], ept_misconfig_pte[3]); } #endif /* DEBUG_EPT_MISCONFIG */ return (VMEXIT_ABORT); } static int vmexit_svm(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { fprintf(stderr, "vm exit[%d]\n", *pvcpu); fprintf(stderr, "\treason\t\tSVM\n"); fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip); fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length); fprintf(stderr, "\texitcode\t%#lx\n", vmexit->u.svm.exitcode); fprintf(stderr, "\texitinfo1\t%#lx\n", vmexit->u.svm.exitinfo1); fprintf(stderr, "\texitinfo2\t%#lx\n", vmexit->u.svm.exitinfo2); return (VMEXIT_ABORT); } static int vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { assert(vmexit->inst_length == 0); stats.vmexit_bogus++; return (VMEXIT_CONTINUE); } static int vmexit_reqidle(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { assert(vmexit->inst_length == 0); stats.vmexit_reqidle++; return (VMEXIT_CONTINUE); } static int vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { stats.vmexit_hlt++; /* * Just continue execution with the next instruction. We use * the HLT VM exit as a way to be friendly with the host * scheduler. */ return (VMEXIT_CONTINUE); } static int vmexit_pause(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { stats.vmexit_pause++; return (VMEXIT_CONTINUE); } static int vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { assert(vmexit->inst_length == 0); stats.vmexit_mtrap++; return (VMEXIT_CONTINUE); } static int vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { int err, i; struct vie *vie; stats.vmexit_inst_emul++; vie = &vmexit->u.inst_emul.vie; err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa, vie, &vmexit->u.inst_emul.paging); if (err) { if (err == ESRCH) { fprintf(stderr, "Unhandled memory access to 0x%lx\n", vmexit->u.inst_emul.gpa); } fprintf(stderr, "Failed to emulate instruction ["); for (i = 0; i < vie->num_valid; i++) { fprintf(stderr, "0x%02x%s", vie->inst[i], i != (vie->num_valid - 1) ? " " : ""); } fprintf(stderr, "] at 0x%lx\n", vmexit->rip); return (VMEXIT_ABORT); } return (VMEXIT_CONTINUE); } static pthread_mutex_t resetcpu_mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t resetcpu_cond = PTHREAD_COND_INITIALIZER; static int vmexit_suspend(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { enum vm_suspend_how how; how = vmexit->u.suspended.how; fbsdrun_deletecpu(ctx, *pvcpu); if (*pvcpu != BSP) { pthread_mutex_lock(&resetcpu_mtx); pthread_cond_signal(&resetcpu_cond); pthread_mutex_unlock(&resetcpu_mtx); pthread_exit(NULL); } pthread_mutex_lock(&resetcpu_mtx); while (!CPU_EMPTY(&cpumask)) { pthread_cond_wait(&resetcpu_cond, &resetcpu_mtx); } pthread_mutex_unlock(&resetcpu_mtx); switch (how) { case VM_SUSPEND_RESET: exit(0); case VM_SUSPEND_POWEROFF: exit(1); case VM_SUSPEND_HALT: exit(2); case VM_SUSPEND_TRIPLEFAULT: exit(3); default: fprintf(stderr, "vmexit_suspend: invalid reason %d\n", how); exit(100); } return (0); /* NOTREACHED */ } static vmexit_handler_t handler[VM_EXITCODE_MAX] = { [VM_EXITCODE_INOUT] = vmexit_inout, [VM_EXITCODE_INOUT_STR] = vmexit_inout, [VM_EXITCODE_VMX] = vmexit_vmx, [VM_EXITCODE_SVM] = vmexit_svm, [VM_EXITCODE_BOGUS] = vmexit_bogus, [VM_EXITCODE_REQIDLE] = vmexit_reqidle, [VM_EXITCODE_RDMSR] = vmexit_rdmsr, [VM_EXITCODE_WRMSR] = vmexit_wrmsr, [VM_EXITCODE_MTRAP] = vmexit_mtrap, [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, [VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap, [VM_EXITCODE_SUSPENDED] = vmexit_suspend, [VM_EXITCODE_TASK_SWITCH] = vmexit_task_switch, }; static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t startrip) { - int error, rc, prevcpu; + int error, rc; enum vm_exitcode exitcode; cpuset_t active_cpus; if (vcpumap[vcpu] != NULL) { error = pthread_setaffinity_np(pthread_self(), sizeof(cpuset_t), vcpumap[vcpu]); assert(error == 0); } error = vm_active_cpus(ctx, &active_cpus); assert(CPU_ISSET(vcpu, &active_cpus)); error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RIP, startrip); assert(error == 0); while (1) { error = vm_run(ctx, vcpu, &vmexit[vcpu]); if (error != 0) break; - prevcpu = vcpu; - exitcode = vmexit[vcpu].exitcode; if (exitcode >= VM_EXITCODE_MAX || handler[exitcode] == NULL) { fprintf(stderr, "vm_loop: unexpected exitcode 0x%x\n", exitcode); exit(1); } - rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu); + rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu); switch (rc) { case VMEXIT_CONTINUE: break; case VMEXIT_ABORT: abort(); default: exit(1); } } fprintf(stderr, "vm_run error %d, errno %d\n", error, errno); } static int num_vcpus_allowed(struct vmctx *ctx) { int tmp, error; error = vm_get_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, &tmp); /* * The guest is allowed to spinup more than one processor only if the * UNRESTRICTED_GUEST capability is available. */ if (error == 0) return (VM_MAXCPU); else return (1); } void fbsdrun_set_capabilities(struct vmctx *ctx, int cpu) { int err, tmp; if (fbsdrun_vmexit_on_hlt()) { err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp); if (err < 0) { fprintf(stderr, "VM exit on HLT not supported\n"); exit(1); } vm_set_capability(ctx, cpu, VM_CAP_HALT_EXIT, 1); if (cpu == BSP) handler[VM_EXITCODE_HLT] = vmexit_hlt; } if (fbsdrun_vmexit_on_pause()) { /* * pause exit support required for this mode */ err = vm_get_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, &tmp); if (err < 0) { fprintf(stderr, "SMP mux requested, no pause support\n"); exit(1); } vm_set_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, 1); if (cpu == BSP) handler[VM_EXITCODE_PAUSE] = vmexit_pause; } if (x2apic_mode) err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED); else err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED); if (err) { fprintf(stderr, "Unable to set x2apic state (%d)\n", err); exit(1); } vm_set_capability(ctx, cpu, VM_CAP_ENABLE_INVPCID, 1); } static struct vmctx * do_open(const char *vmname) { struct vmctx *ctx; int error; bool reinit, romboot; reinit = romboot = false; if (lpc_bootrom()) romboot = true; error = vm_create(vmname); if (error) { if (errno == EEXIST) { if (romboot) { reinit = true; } else { /* * The virtual machine has been setup by the * userspace bootloader. */ } } else { perror("vm_create"); exit(1); } } else { if (!romboot) { /* * If the virtual machine was just created then a * bootrom must be configured to boot it. */ fprintf(stderr, "virtual machine cannot be booted\n"); exit(1); } } ctx = vm_open(vmname); if (ctx == NULL) { perror("vm_open"); exit(1); } if (reinit) { error = vm_reinit(ctx); if (error) { perror("vm_reinit"); exit(1); } } return (ctx); } int main(int argc, char *argv[]) { int c, error, gdb_port, err, bvmcons; int max_vcpus, mptgen, memflags; int rtc_localtime; struct vmctx *ctx; uint64_t rip; size_t memsize; char *optstr; bvmcons = 0; progname = basename(argv[0]); gdb_port = 0; guest_ncpus = 1; memsize = 256 * MB; mptgen = 1; rtc_localtime = 1; memflags = 0; optstr = "abehuwxACHIPSWYp:g:c:s:m:l:U:"; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'a': x2apic_mode = 0; break; case 'A': acpi = 1; break; case 'b': bvmcons = 1; break; case 'p': if (pincpu_parse(optarg) != 0) { errx(EX_USAGE, "invalid vcpu pinning " "configuration '%s'", optarg); } break; case 'c': guest_ncpus = atoi(optarg); break; case 'C': memflags |= VM_MEM_F_INCORE; break; case 'g': gdb_port = atoi(optarg); break; case 'l': if (lpc_device_parse(optarg) != 0) { errx(EX_USAGE, "invalid lpc device " "configuration '%s'", optarg); } break; case 's': if (pci_parse_slot(optarg) != 0) exit(1); else break; case 'S': memflags |= VM_MEM_F_WIRED; break; case 'm': error = vm_parse_memsize(optarg, &memsize); if (error) errx(EX_USAGE, "invalid memsize '%s'", optarg); break; case 'H': guest_vmexit_on_hlt = 1; break; case 'I': /* * The "-I" option was used to add an ioapic to the * virtual machine. * * An ioapic is now provided unconditionally for each * virtual machine and this option is now deprecated. */ break; case 'P': guest_vmexit_on_pause = 1; break; case 'e': strictio = 1; break; case 'u': rtc_localtime = 0; break; case 'U': guest_uuid_str = optarg; break; case 'w': strictmsr = 0; break; case 'W': virtio_msix = 0; break; case 'x': x2apic_mode = 1; break; case 'Y': mptgen = 0; break; case 'h': usage(0); default: usage(1); } } argc -= optind; argv += optind; if (argc != 1) usage(1); vmname = argv[0]; ctx = do_open(vmname); if (guest_ncpus < 1) { fprintf(stderr, "Invalid guest vCPUs (%d)\n", guest_ncpus); exit(1); } max_vcpus = num_vcpus_allowed(ctx); if (guest_ncpus > max_vcpus) { fprintf(stderr, "%d vCPUs requested but only %d available\n", guest_ncpus, max_vcpus); exit(1); } fbsdrun_set_capabilities(ctx, BSP); vm_set_memflags(ctx, memflags); err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); if (err) { fprintf(stderr, "Unable to setup memory (%d)\n", errno); exit(1); } error = init_msr(); if (error) { fprintf(stderr, "init_msr error %d", error); exit(1); } init_mem(); init_inout(); pci_irq_init(ctx); ioapic_init(ctx); rtc_init(ctx, rtc_localtime); sci_init(ctx); /* * Exit if a device emulation finds an error in it's initilization */ if (init_pci(ctx) != 0) exit(1); if (gdb_port != 0) init_dbgport(gdb_port); if (bvmcons) init_bvmcons(); if (lpc_bootrom()) { if (vm_set_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, 1)) { fprintf(stderr, "ROM boot failed: unrestricted guest " "capability not available\n"); exit(1); } error = vcpu_reset(ctx, BSP); assert(error == 0); } error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); assert(error == 0); /* * build the guest tables, MP etc. */ if (mptgen) { error = mptable_build(ctx, guest_ncpus); if (error) exit(1); } error = smbios_build(ctx); assert(error == 0); if (acpi) { error = acpi_build(ctx, guest_ncpus); assert(error == 0); } if (lpc_bootrom()) fwctl_init(); /* * Change the proc title to include the VM name. */ setproctitle("%s", vmname); /* * Add CPU 0 */ fbsdrun_addcpu(ctx, BSP, BSP, rip); /* * Head off to the main event dispatch loop */ mevent_dispatch(); exit(1); } Index: projects/clang380-import/usr.sbin/bhyve/pci_emul.c =================================================================== --- projects/clang380-import/usr.sbin/bhyve/pci_emul.c (revision 293015) +++ projects/clang380-import/usr.sbin/bhyve/pci_emul.c (revision 293016) @@ -1,2108 +1,2107 @@ /*- * Copyright (c) 2011 NetApp, 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. * 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 NETAPP, INC ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC 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$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi.h" #include "bhyverun.h" #include "inout.h" #include "ioapic.h" #include "mem.h" #include "pci_emul.h" #include "pci_irq.h" #include "pci_lpc.h" #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc #define CONF1_ENABLE 0x80000000ul #define MAXBUSES (PCI_BUSMAX + 1) #define MAXSLOTS (PCI_SLOTMAX + 1) #define MAXFUNCS (PCI_FUNCMAX + 1) struct funcinfo { char *fi_name; char *fi_param; struct pci_devinst *fi_devi; }; struct intxinfo { int ii_count; int ii_pirq_pin; int ii_ioapic_irq; }; struct slotinfo { struct intxinfo si_intpins[4]; struct funcinfo si_funcs[MAXFUNCS]; }; struct businfo { uint16_t iobase, iolimit; /* I/O window */ uint32_t membase32, memlimit32; /* mmio window below 4GB */ uint64_t membase64, memlimit64; /* mmio window above 4GB */ struct slotinfo slotinfo[MAXSLOTS]; }; static struct businfo *pci_businfo[MAXBUSES]; SET_DECLARE(pci_devemu_set, struct pci_devemu); static uint64_t pci_emul_iobase; static uint64_t pci_emul_membase32; static uint64_t pci_emul_membase64; #define PCI_EMUL_IOBASE 0x2000 #define PCI_EMUL_IOLIMIT 0x10000 #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE #define PCI_EMUL_MEMBASE64 0xD000000000UL #define PCI_EMUL_MEMLIMIT64 0xFD00000000UL static struct pci_devemu *pci_emul_finddev(char *name); static void pci_lintr_route(struct pci_devinst *pi); static void pci_lintr_update(struct pci_devinst *pi); static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, int coff, int bytes, uint32_t *val); static __inline void CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) { if (bytes == 1) pci_set_cfgdata8(pi, coff, val); else if (bytes == 2) pci_set_cfgdata16(pi, coff, val); else pci_set_cfgdata32(pi, coff, val); } static __inline uint32_t CFGREAD(struct pci_devinst *pi, int coff, int bytes) { if (bytes == 1) return (pci_get_cfgdata8(pi, coff)); else if (bytes == 2) return (pci_get_cfgdata16(pi, coff)); else return (pci_get_cfgdata32(pi, coff)); } /* * I/O access */ /* * Slot options are in the form: * * ::,[,] * [:],[,] * * slot is 0..31 * func is 0..7 * emul is a string describing the type of PCI device e.g. virtio-net * config is an optional string, depending on the device, that can be * used for configuration. * Examples are: * 1,virtio-net,tap0 * 3:0,dummy */ static void pci_parse_slot_usage(char *aopt) { fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); } int pci_parse_slot(char *opt) { struct businfo *bi; struct slotinfo *si; char *emul, *config, *str, *cp; int error, bnum, snum, fnum; error = -1; str = strdup(opt); emul = config = NULL; if ((cp = strchr(str, ',')) != NULL) { *cp = '\0'; emul = cp + 1; if ((cp = strchr(emul, ',')) != NULL) { *cp = '\0'; config = cp + 1; } } else { pci_parse_slot_usage(opt); goto done; } /* :: */ if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { bnum = 0; /* : */ if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { fnum = 0; /* */ if (sscanf(str, "%d", &snum) != 1) { snum = -1; } } } if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) { pci_parse_slot_usage(opt); goto done; } if (pci_businfo[bnum] == NULL) pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); bi = pci_businfo[bnum]; si = &bi->slotinfo[snum]; if (si->si_funcs[fnum].fi_name != NULL) { fprintf(stderr, "pci slot %d:%d already occupied!\n", snum, fnum); goto done; } if (pci_emul_finddev(emul) == NULL) { fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", snum, fnum, emul); goto done; } error = 0; si->si_funcs[fnum].fi_name = emul; si->si_funcs[fnum].fi_param = config; done: if (error) free(str); return (error); } static int pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) { if (offset < pi->pi_msix.pba_offset) return (0); if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { return (0); } return (1); } int pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, uint64_t value) { int msix_entry_offset; int tab_index; char *dest; /* support only 4 or 8 byte writes */ if (size != 4 && size != 8) return (-1); /* * Return if table index is beyond what device supports */ tab_index = offset / MSIX_TABLE_ENTRY_SIZE; if (tab_index >= pi->pi_msix.table_count) return (-1); msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; /* support only aligned writes */ if ((msix_entry_offset % size) != 0) return (-1); dest = (char *)(pi->pi_msix.table + tab_index); dest += msix_entry_offset; if (size == 4) *((uint32_t *)dest) = value; else *((uint64_t *)dest) = value; return (0); } uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) { char *dest; int msix_entry_offset; int tab_index; uint64_t retval = ~0; /* * The PCI standard only allows 4 and 8 byte accesses to the MSI-X * table but we also allow 1 byte access to accommodate reads from * ddb. */ if (size != 1 && size != 4 && size != 8) return (retval); msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; /* support only aligned reads */ if ((msix_entry_offset % size) != 0) { return (retval); } tab_index = offset / MSIX_TABLE_ENTRY_SIZE; if (tab_index < pi->pi_msix.table_count) { /* valid MSI-X Table access */ dest = (char *)(pi->pi_msix.table + tab_index); dest += msix_entry_offset; if (size == 1) retval = *((uint8_t *)dest); else if (size == 4) retval = *((uint32_t *)dest); else retval = *((uint64_t *)dest); } else if (pci_valid_pba_offset(pi, offset)) { /* return 0 for PBA access */ retval = 0; } return (retval); } int pci_msix_table_bar(struct pci_devinst *pi) { if (pi->pi_msix.table != NULL) return (pi->pi_msix.table_bar); else return (-1); } int pci_msix_pba_bar(struct pci_devinst *pi) { if (pi->pi_msix.table != NULL) return (pi->pi_msix.pba_bar); else return (-1); } static int pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { struct pci_devinst *pdi = arg; struct pci_devemu *pe = pdi->pi_d; uint64_t offset; int i; for (i = 0; i <= PCI_BARMAX; i++) { if (pdi->pi_bar[i].type == PCIBAR_IO && port >= pdi->pi_bar[i].addr && port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { offset = port - pdi->pi_bar[i].addr; if (in) *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, offset, bytes); else (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, bytes, *eax); return (0); } } return (-1); } static int pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int size, uint64_t *val, void *arg1, long arg2) { struct pci_devinst *pdi = arg1; struct pci_devemu *pe = pdi->pi_d; uint64_t offset; int bidx = (int) arg2; assert(bidx <= PCI_BARMAX); assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || pdi->pi_bar[bidx].type == PCIBAR_MEM64); assert(addr >= pdi->pi_bar[bidx].addr && addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); offset = addr - pdi->pi_bar[bidx].addr; if (dir == MEM_F_WRITE) { if (size == 8) { (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 4, *val & 0xffffffff); (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 4, *val >> 32); } else { (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val); } } else { if (size == 8) { *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, 4); *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset + 4, 4) << 32; } else { *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size); } } return (0); } static int pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, uint64_t *addr) { uint64_t base; assert((size & (size - 1)) == 0); /* must be a power of 2 */ base = roundup2(*baseptr, size); if (base + size <= limit) { *addr = base; *baseptr = base + size; return (0); } else return (-1); } int pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, uint64_t size) { return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); } /* * Register (or unregister) the MMIO or I/O region associated with the BAR * register 'idx' of an emulated pci device. */ static void modify_bar_registration(struct pci_devinst *pi, int idx, int registration) { int error; struct inout_port iop; struct mem_range mr; switch (pi->pi_bar[idx].type) { case PCIBAR_IO: bzero(&iop, sizeof(struct inout_port)); iop.name = pi->pi_name; iop.port = pi->pi_bar[idx].addr; iop.size = pi->pi_bar[idx].size; if (registration) { iop.flags = IOPORT_F_INOUT; iop.handler = pci_emul_io_handler; iop.arg = pi; error = register_inout(&iop); } else error = unregister_inout(&iop); break; case PCIBAR_MEM32: case PCIBAR_MEM64: bzero(&mr, sizeof(struct mem_range)); mr.name = pi->pi_name; mr.base = pi->pi_bar[idx].addr; mr.size = pi->pi_bar[idx].size; if (registration) { mr.flags = MEM_F_RW; mr.handler = pci_emul_mem_handler; mr.arg1 = pi; mr.arg2 = idx; error = register_mem(&mr); } else error = unregister_mem(&mr); break; default: error = EINVAL; break; } assert(error == 0); } static void unregister_bar(struct pci_devinst *pi, int idx) { modify_bar_registration(pi, idx, 0); } static void register_bar(struct pci_devinst *pi, int idx) { modify_bar_registration(pi, idx, 1); } /* Are we decoding i/o port accesses for the emulated pci device? */ static int porten(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (cmd & PCIM_CMD_PORTEN); } /* Are we decoding memory accesses for the emulated pci device? */ static int memen(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (cmd & PCIM_CMD_MEMEN); } /* * Update the MMIO or I/O address that is decoded by the BAR register. * * If the pci device has enabled the address space decoding then intercept * the address range decoded by the BAR register. */ static void update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) { int decode; if (pi->pi_bar[idx].type == PCIBAR_IO) decode = porten(pi); else decode = memen(pi); if (decode) unregister_bar(pi, idx); switch (type) { case PCIBAR_IO: case PCIBAR_MEM32: pi->pi_bar[idx].addr = addr; break; case PCIBAR_MEM64: pi->pi_bar[idx].addr &= ~0xffffffffUL; pi->pi_bar[idx].addr |= addr; break; case PCIBAR_MEMHI64: pi->pi_bar[idx].addr &= 0xffffffff; pi->pi_bar[idx].addr |= addr; break; default: assert(0); } if (decode) register_bar(pi, idx); } int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size) { int error; uint64_t *baseptr, limit, addr, mask, lobits, bar; assert(idx >= 0 && idx <= PCI_BARMAX); if ((size & (size - 1)) != 0) size = 1UL << flsl(size); /* round up to a power of 2 */ /* Enforce minimum BAR sizes required by the PCI standard */ if (type == PCIBAR_IO) { if (size < 4) size = 4; } else { if (size < 16) size = 16; } switch (type) { case PCIBAR_NONE: baseptr = NULL; addr = mask = lobits = 0; break; case PCIBAR_IO: baseptr = &pci_emul_iobase; limit = PCI_EMUL_IOLIMIT; mask = PCIM_BAR_IO_BASE; lobits = PCIM_BAR_IO_SPACE; break; case PCIBAR_MEM64: /* * XXX * Some drivers do not work well if the 64-bit BAR is allocated * above 4GB. Allow for this by allocating small requests under * 4GB unless then allocation size is larger than some arbitrary * number (32MB currently). */ if (size > 32 * 1024 * 1024) { /* * XXX special case for device requiring peer-peer DMA */ if (size == 0x100000000UL) baseptr = &hostbase; else baseptr = &pci_emul_membase64; limit = PCI_EMUL_MEMLIMIT64; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | PCIM_BAR_MEM_PREFETCH; break; } else { baseptr = &pci_emul_membase32; limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; } break; case PCIBAR_MEM32: baseptr = &pci_emul_membase32; limit = PCI_EMUL_MEMLIMIT32; mask = PCIM_BAR_MEM_BASE; lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; break; default: printf("pci_emul_alloc_base: invalid bar type %d\n", type); assert(0); } if (baseptr != NULL) { error = pci_emul_alloc_resource(baseptr, limit, size, &addr); if (error != 0) return (error); } pdi->pi_bar[idx].type = type; pdi->pi_bar[idx].addr = addr; pdi->pi_bar[idx].size = size; /* Initialize the BAR register in config space */ bar = (addr & mask) | lobits; pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); if (type == PCIBAR_MEM64) { assert(idx + 1 <= PCI_BARMAX); pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); } register_bar(pdi, idx); return (0); } #define CAP_START_OFFSET 0x40 static int pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) { int i, capoff, reallen; uint16_t sts; assert(caplen > 0); reallen = roundup2(caplen, 4); /* dword aligned */ sts = pci_get_cfgdata16(pi, PCIR_STATUS); if ((sts & PCIM_STATUS_CAPPRESENT) == 0) capoff = CAP_START_OFFSET; else capoff = pi->pi_capend + 1; /* Check if we have enough space */ if (capoff + reallen > PCI_REGMAX + 1) return (-1); /* Set the previous capability pointer */ if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); } else pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); /* Copy the capability */ for (i = 0; i < caplen; i++) pci_set_cfgdata8(pi, capoff + i, capdata[i]); /* Set the next capability pointer */ pci_set_cfgdata8(pi, capoff + 1, 0); pi->pi_prevcap = capoff; pi->pi_capend = capoff + reallen - 1; return (0); } static struct pci_devemu * pci_emul_finddev(char *name) { struct pci_devemu **pdpp, *pdp; SET_FOREACH(pdpp, pci_devemu_set) { pdp = *pdpp; if (!strcmp(pdp->pe_emu, name)) { return (pdp); } } return (NULL); } static int pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, int func, struct funcinfo *fi) { struct pci_devinst *pdi; int err; pdi = calloc(1, sizeof(struct pci_devinst)); pdi->pi_vmctx = ctx; pdi->pi_bus = bus; pdi->pi_slot = slot; pdi->pi_func = func; pthread_mutex_init(&pdi->pi_lintr.lock, NULL); pdi->pi_lintr.pin = 0; pdi->pi_lintr.state = IDLE; pdi->pi_lintr.pirq_pin = 0; pdi->pi_lintr.ioapic_irq = 0; pdi->pi_d = pde; snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); /* Disable legacy interrupts */ pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); err = (*pde->pe_init)(ctx, pdi, fi->fi_param); if (err == 0) fi->fi_devi = pdi; else free(pdi); return (err); } void pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) { int mmc; CTASSERT(sizeof(struct msicap) == 14); /* Number of msi messages must be a power of 2 between 1 and 32 */ assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); mmc = ffs(msgnum) - 1; bzero(msicap, sizeof(struct msicap)); msicap->capid = PCIY_MSI; msicap->nextptr = nextptr; msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); } int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) { struct msicap msicap; pci_populate_msicap(&msicap, msgnum, 0); return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); } static void pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, uint32_t msix_tab_size) { CTASSERT(sizeof(struct msixcap) == 12); assert(msix_tab_size % 4096 == 0); bzero(msixcap, sizeof(struct msixcap)); msixcap->capid = PCIY_MSIX; /* * Message Control Register, all fields set to * zero except for the Table Size. * Note: Table size N is encoded as N-1 */ msixcap->msgctrl = msgnum - 1; /* * MSI-X BAR setup: * - MSI-X table start at offset 0 * - PBA table starts at a 4K aligned offset after the MSI-X table */ msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); } static void pci_msix_table_init(struct pci_devinst *pi, int table_entries) { int i, table_size; assert(table_entries > 0); assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; pi->pi_msix.table = calloc(1, table_size); /* set mask bit of vector control register */ for (i = 0; i < table_entries; i++) pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; } int pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) { uint32_t tab_size; struct msixcap msixcap; assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; /* Align table size to nearest 4K */ tab_size = roundup2(tab_size, 4096); pi->pi_msix.table_bar = barnum; pi->pi_msix.pba_bar = barnum; pi->pi_msix.table_offset = 0; pi->pi_msix.table_count = msgnum; pi->pi_msix.pba_offset = tab_size; pi->pi_msix.pba_size = PBA_SIZE(msgnum); pci_msix_table_init(pi, msgnum); pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); /* allocate memory for MSI-X Table and PBA */ pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, tab_size + pi->pi_msix.pba_size); return (pci_emul_add_capability(pi, (u_char *)&msixcap, sizeof(msixcap))); } void msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { uint16_t msgctrl, rwmask; - int off, table_bar; + int off; off = offset - capoff; - table_bar = pi->pi_msix.table_bar; /* Message Control Register */ if (off == 2 && bytes == 2) { rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; msgctrl = pci_get_cfgdata16(pi, offset); msgctrl &= ~rwmask; msgctrl |= val & rwmask; val = msgctrl; pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); } void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { uint16_t msgctrl, rwmask, msgdata, mme; uint32_t addrlo; /* * If guest is writing to the message control register make sure * we do not overwrite read-only fields. */ if ((offset - capoff) == 2 && bytes == 2) { rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; msgctrl = pci_get_cfgdata16(pi, offset); msgctrl &= ~rwmask; msgctrl |= val & rwmask; val = msgctrl; addrlo = pci_get_cfgdata32(pi, capoff + 4); if (msgctrl & PCIM_MSICTRL_64BIT) msgdata = pci_get_cfgdata16(pi, capoff + 12); else msgdata = pci_get_cfgdata16(pi, capoff + 8); mme = msgctrl & PCIM_MSICTRL_MME_MASK; pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; if (pi->pi_msi.enabled) { pi->pi_msi.addr = addrlo; pi->pi_msi.msg_data = msgdata; pi->pi_msi.maxmsgnum = 1 << (mme >> 4); } else { pi->pi_msi.maxmsgnum = 0; } pci_lintr_update(pi); } CFGWRITE(pi, offset, val, bytes); } void pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, int bytes, uint32_t val) { /* XXX don't write to the readonly parts */ CFGWRITE(pi, offset, val, bytes); } #define PCIECAP_VERSION 0x2 int pci_emul_add_pciecap(struct pci_devinst *pi, int type) { int err; struct pciecap pciecap; CTASSERT(sizeof(struct pciecap) == 60); if (type != PCIEM_TYPE_ROOT_PORT) return (-1); bzero(&pciecap, sizeof(pciecap)); pciecap.capid = PCIY_EXPRESS; pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; pciecap.link_capabilities = 0x411; /* gen1, x1 */ pciecap.link_status = 0x11; /* gen1, x1 */ err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); return (err); } /* * This function assumes that 'coff' is in the capabilities region of the * config space. */ static void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) { int capid; uint8_t capoff, nextoff; /* Do not allow un-aligned writes */ if ((offset & (bytes - 1)) != 0) return; /* Find the capability that we want to update */ capoff = CAP_START_OFFSET; while (1) { nextoff = pci_get_cfgdata8(pi, capoff + 1); if (nextoff == 0) break; if (offset >= capoff && offset < nextoff) break; capoff = nextoff; } assert(offset >= capoff); /* * Capability ID and Next Capability Pointer are readonly. * However, some o/s's do 4-byte writes that include these. * For this case, trim the write back to 2 bytes and adjust * the data. */ if (offset == capoff || offset == capoff + 1) { if (offset == capoff && bytes == 4) { bytes = 2; offset += 2; val >>= 16; } else return; } capid = pci_get_cfgdata8(pi, capoff); switch (capid) { case PCIY_MSI: msicap_cfgwrite(pi, capoff, offset, bytes, val); break; case PCIY_MSIX: msixcap_cfgwrite(pi, capoff, offset, bytes, val); break; case PCIY_EXPRESS: pciecap_cfgwrite(pi, capoff, offset, bytes, val); break; default: break; } } static int pci_emul_iscap(struct pci_devinst *pi, int offset) { uint16_t sts; sts = pci_get_cfgdata16(pi, PCIR_STATUS); if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) return (1); } return (0); } static int pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int size, uint64_t *val, void *arg1, long arg2) { /* * Ignore writes; return 0xff's for reads. The mem read code * will take care of truncating to the correct size. */ if (dir == MEM_F_READ) { *val = 0xffffffffffffffff; } return (0); } static int pci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int bytes, uint64_t *val, void *arg1, long arg2) { int bus, slot, func, coff, in; coff = addr & 0xfff; func = (addr >> 12) & 0x7; slot = (addr >> 15) & 0x1f; bus = (addr >> 20) & 0xff; in = (dir == MEM_F_READ); if (in) *val = ~0UL; pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); return (0); } uint64_t pci_ecfg_base(void) { return (PCI_EMUL_ECFG_BASE); } #define BUSIO_ROUNDUP 32 #define BUSMEM_ROUNDUP (1024 * 1024) int init_pci(struct vmctx *ctx) { struct mem_range mr; struct pci_devemu *pde; struct businfo *bi; struct slotinfo *si; struct funcinfo *fi; size_t lowmem; int bus, slot, func; int error; pci_emul_iobase = PCI_EMUL_IOBASE; pci_emul_membase32 = vm_get_lowmem_limit(ctx); pci_emul_membase64 = PCI_EMUL_MEMBASE64; for (bus = 0; bus < MAXBUSES; bus++) { if ((bi = pci_businfo[bus]) == NULL) continue; /* * Keep track of the i/o and memory resources allocated to * this bus. */ bi->iobase = pci_emul_iobase; bi->membase32 = pci_emul_membase32; bi->membase64 = pci_emul_membase64; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { fi = &si->si_funcs[func]; if (fi->fi_name == NULL) continue; pde = pci_emul_finddev(fi->fi_name); assert(pde != NULL); error = pci_emul_init(ctx, pde, bus, slot, func, fi); if (error) return (error); } } /* * Add some slop to the I/O and memory resources decoded by * this bus to give a guest some flexibility if it wants to * reprogram the BARs. */ pci_emul_iobase += BUSIO_ROUNDUP; pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); bi->iolimit = pci_emul_iobase; pci_emul_membase32 += BUSMEM_ROUNDUP; pci_emul_membase32 = roundup2(pci_emul_membase32, BUSMEM_ROUNDUP); bi->memlimit32 = pci_emul_membase32; pci_emul_membase64 += BUSMEM_ROUNDUP; pci_emul_membase64 = roundup2(pci_emul_membase64, BUSMEM_ROUNDUP); bi->memlimit64 = pci_emul_membase64; } /* * PCI backends are initialized before routing INTx interrupts * so that LPC devices are able to reserve ISA IRQs before * routing PIRQ pins. */ for (bus = 0; bus < MAXBUSES; bus++) { if ((bi = pci_businfo[bus]) == NULL) continue; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { fi = &si->si_funcs[func]; if (fi->fi_devi == NULL) continue; pci_lintr_route(fi->fi_devi); } } } lpc_pirq_routed(); /* * The guest physical memory map looks like the following: * [0, lowmem) guest system memory * [lowmem, lowmem_limit) memory hole (may be absent) * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) * [0xE0000000, 0xF0000000) PCI extended config window * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware * [4GB, 4GB + highmem) */ /* * Accesses to memory addresses that are not allocated to system * memory or PCI devices return 0xff's. */ lowmem = vm_get_lowmem_size(ctx); bzero(&mr, sizeof(struct mem_range)); mr.name = "PCI hole"; mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; mr.base = lowmem; mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; mr.handler = pci_emul_fallback_handler; error = register_mem_fallback(&mr); assert(error == 0); /* PCI extended config space */ bzero(&mr, sizeof(struct mem_range)); mr.name = "PCI ECFG"; mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; mr.base = PCI_EMUL_ECFG_BASE; mr.size = PCI_EMUL_ECFG_SIZE; mr.handler = pci_emul_ecfg_handler; error = register_mem(&mr); assert(error == 0); return (0); } static void pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, void *arg) { dsdt_line(" Package ()"); dsdt_line(" {"); dsdt_line(" 0x%X,", slot << 16 | 0xffff); dsdt_line(" 0x%02X,", pin - 1); dsdt_line(" Zero,"); dsdt_line(" 0x%X", ioapic_irq); dsdt_line(" },"); } static void pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, void *arg) { char *name; name = lpc_pirq_name(pirq_pin); if (name == NULL) return; dsdt_line(" Package ()"); dsdt_line(" {"); dsdt_line(" 0x%X,", slot << 16 | 0xffff); dsdt_line(" 0x%02X,", pin - 1); dsdt_line(" %s,", name); dsdt_line(" 0x00"); dsdt_line(" },"); free(name); } /* * A bhyve virtual machine has a flat PCI hierarchy with a root port * corresponding to each PCI bus. */ static void pci_bus_write_dsdt(int bus) { struct businfo *bi; struct slotinfo *si; struct pci_devinst *pi; int count, func, slot; /* * If there are no devices on this 'bus' then just return. */ if ((bi = pci_businfo[bus]) == NULL) { /* * Bus 0 is special because it decodes the I/O ports used * for PCI config space access even if there are no devices * on it. */ if (bus != 0) return; } dsdt_line(" Device (PC%02X)", bus); dsdt_line(" {"); dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); dsdt_line(" Name (_ADR, Zero)"); dsdt_line(" Method (_BBN, 0, NotSerialized)"); dsdt_line(" {"); dsdt_line(" Return (0x%08X)", bus); dsdt_line(" }"); dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " "MaxFixed, PosDecode,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x%04X, // Range Minimum", bus); dsdt_line(" 0x%04X, // Range Maximum", bus); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x0001, // Length"); dsdt_line(" ,, )"); if (bus == 0) { dsdt_indent(3); dsdt_fixed_ioport(0xCF8, 8); dsdt_unindent(3); dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x0000, // Range Minimum"); dsdt_line(" 0x0CF7, // Range Maximum"); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x0CF8, // Length"); dsdt_line(" ,, , TypeStatic)"); dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x0D00, // Range Minimum"); dsdt_line(" 0x%04X, // Range Maximum", PCI_EMUL_IOBASE - 1); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x%04X, // Length", PCI_EMUL_IOBASE - 0x0D00); dsdt_line(" ,, , TypeStatic)"); if (bi == NULL) { dsdt_line(" })"); goto done; } } assert(bi != NULL); /* i/o window */ dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " "PosDecode, EntireRange,"); dsdt_line(" 0x0000, // Granularity"); dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); dsdt_line(" 0x%04X, // Range Maximum", bi->iolimit - 1); dsdt_line(" 0x0000, // Translation Offset"); dsdt_line(" 0x%04X, // Length", bi->iolimit - bi->iobase); dsdt_line(" ,, , TypeStatic)"); /* mmio window (32-bit) */ dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); dsdt_line(" 0x00000000, // Granularity"); dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); dsdt_line(" 0x%08X, // Range Maximum\n", bi->memlimit32 - 1); dsdt_line(" 0x00000000, // Translation Offset"); dsdt_line(" 0x%08X, // Length\n", bi->memlimit32 - bi->membase32); dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); /* mmio window (64-bit) */ dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); dsdt_line(" 0x0000000000000000, // Granularity"); dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); dsdt_line(" 0x%016lX, // Range Maximum\n", bi->memlimit64 - 1); dsdt_line(" 0x0000000000000000, // Translation Offset"); dsdt_line(" 0x%016lX, // Length\n", bi->memlimit64 - bi->membase64); dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); dsdt_line(" })"); count = pci_count_lintr(bus); if (count != 0) { dsdt_indent(2); dsdt_line("Name (PPRT, Package ()"); dsdt_line("{"); pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); dsdt_line("})"); dsdt_line("Name (APRT, Package ()"); dsdt_line("{"); pci_walk_lintr(bus, pci_apic_prt_entry, NULL); dsdt_line("})"); dsdt_line("Method (_PRT, 0, NotSerialized)"); dsdt_line("{"); dsdt_line(" If (PICM)"); dsdt_line(" {"); dsdt_line(" Return (APRT)"); dsdt_line(" }"); dsdt_line(" Else"); dsdt_line(" {"); dsdt_line(" Return (PPRT)"); dsdt_line(" }"); dsdt_line("}"); dsdt_unindent(2); } dsdt_indent(2); for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (func = 0; func < MAXFUNCS; func++) { pi = si->si_funcs[func].fi_devi; if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) pi->pi_d->pe_write_dsdt(pi); } } dsdt_unindent(2); done: dsdt_line(" }"); } void pci_write_dsdt(void) { int bus; dsdt_indent(1); dsdt_line("Name (PICM, 0x00)"); dsdt_line("Method (_PIC, 1, NotSerialized)"); dsdt_line("{"); dsdt_line(" Store (Arg0, PICM)"); dsdt_line("}"); dsdt_line(""); dsdt_line("Scope (_SB)"); dsdt_line("{"); for (bus = 0; bus < MAXBUSES; bus++) pci_bus_write_dsdt(bus); dsdt_line("}"); dsdt_unindent(1); } int pci_bus_configured(int bus) { assert(bus >= 0 && bus < MAXBUSES); return (pci_businfo[bus] != NULL); } int pci_msi_enabled(struct pci_devinst *pi) { return (pi->pi_msi.enabled); } int pci_msi_maxmsgnum(struct pci_devinst *pi) { if (pi->pi_msi.enabled) return (pi->pi_msi.maxmsgnum); else return (0); } int pci_msix_enabled(struct pci_devinst *pi) { return (pi->pi_msix.enabled && !pi->pi_msi.enabled); } void pci_generate_msix(struct pci_devinst *pi, int index) { struct msix_table_entry *mte; if (!pci_msix_enabled(pi)) return; if (pi->pi_msix.function_mask) return; if (index >= pi->pi_msix.table_count) return; mte = &pi->pi_msix.table[index]; if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { /* XXX Set PBA bit if interrupt is disabled */ vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); } } void pci_generate_msi(struct pci_devinst *pi, int index) { if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, pi->pi_msi.msg_data + index); } } static bool pci_lintr_permitted(struct pci_devinst *pi) { uint16_t cmd; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || (cmd & PCIM_CMD_INTxDIS))); } void pci_lintr_request(struct pci_devinst *pi) { struct businfo *bi; struct slotinfo *si; int bestpin, bestcount, pin; bi = pci_businfo[pi->pi_bus]; assert(bi != NULL); /* * Just allocate a pin from our slot. The pin will be * assigned IRQs later when interrupts are routed. */ si = &bi->slotinfo[pi->pi_slot]; bestpin = 0; bestcount = si->si_intpins[0].ii_count; for (pin = 1; pin < 4; pin++) { if (si->si_intpins[pin].ii_count < bestcount) { bestpin = pin; bestcount = si->si_intpins[pin].ii_count; } } si->si_intpins[bestpin].ii_count++; pi->pi_lintr.pin = bestpin + 1; pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); } static void pci_lintr_route(struct pci_devinst *pi) { struct businfo *bi; struct intxinfo *ii; if (pi->pi_lintr.pin == 0) return; bi = pci_businfo[pi->pi_bus]; assert(bi != NULL); ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; /* * Attempt to allocate an I/O APIC pin for this intpin if one * is not yet assigned. */ if (ii->ii_ioapic_irq == 0) ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); assert(ii->ii_ioapic_irq > 0); /* * Attempt to allocate a PIRQ pin for this intpin if one is * not yet assigned. */ if (ii->ii_pirq_pin == 0) ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); assert(ii->ii_pirq_pin > 0); pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); } void pci_lintr_assert(struct pci_devinst *pi) { assert(pi->pi_lintr.pin > 0); pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == IDLE) { if (pci_lintr_permitted(pi)) { pi->pi_lintr.state = ASSERTED; pci_irq_assert(pi); } else pi->pi_lintr.state = PENDING; } pthread_mutex_unlock(&pi->pi_lintr.lock); } void pci_lintr_deassert(struct pci_devinst *pi) { assert(pi->pi_lintr.pin > 0); pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == ASSERTED) { pi->pi_lintr.state = IDLE; pci_irq_deassert(pi); } else if (pi->pi_lintr.state == PENDING) pi->pi_lintr.state = IDLE; pthread_mutex_unlock(&pi->pi_lintr.lock); } static void pci_lintr_update(struct pci_devinst *pi) { pthread_mutex_lock(&pi->pi_lintr.lock); if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { pci_irq_deassert(pi); pi->pi_lintr.state = PENDING; } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { pi->pi_lintr.state = ASSERTED; pci_irq_assert(pi); } pthread_mutex_unlock(&pi->pi_lintr.lock); } int pci_count_lintr(int bus) { int count, slot, pin; struct slotinfo *slotinfo; count = 0; if (pci_businfo[bus] != NULL) { for (slot = 0; slot < MAXSLOTS; slot++) { slotinfo = &pci_businfo[bus]->slotinfo[slot]; for (pin = 0; pin < 4; pin++) { if (slotinfo->si_intpins[pin].ii_count != 0) count++; } } } return (count); } void pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) { struct businfo *bi; struct slotinfo *si; struct intxinfo *ii; int slot, pin; if ((bi = pci_businfo[bus]) == NULL) return; for (slot = 0; slot < MAXSLOTS; slot++) { si = &bi->slotinfo[slot]; for (pin = 0; pin < 4; pin++) { ii = &si->si_intpins[pin]; if (ii->ii_count != 0) cb(bus, slot, pin + 1, ii->ii_pirq_pin, ii->ii_ioapic_irq, arg); } } } /* * Return 1 if the emulated device in 'slot' is a multi-function device. * Return 0 otherwise. */ static int pci_emul_is_mfdev(int bus, int slot) { struct businfo *bi; struct slotinfo *si; int f, numfuncs; numfuncs = 0; if ((bi = pci_businfo[bus]) != NULL) { si = &bi->slotinfo[slot]; for (f = 0; f < MAXFUNCS; f++) { if (si->si_funcs[f].fi_devi != NULL) { numfuncs++; } } } return (numfuncs > 1); } /* * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on * whether or not is a multi-function being emulated in the pci 'slot'. */ static void pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) { int mfdev; if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { mfdev = pci_emul_is_mfdev(bus, slot); switch (bytes) { case 1: case 2: *rv &= ~PCIM_MFDEV; if (mfdev) { *rv |= PCIM_MFDEV; } break; case 4: *rv &= ~(PCIM_MFDEV << 16); if (mfdev) { *rv |= (PCIM_MFDEV << 16); } break; } } } static void pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) { int i, rshift; uint32_t cmd, cmd2, changed, old, readonly; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ /* * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. * * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are * 'write 1 to clear'. However these bits are not set to '1' by * any device emulation so it is simpler to treat them as readonly. */ rshift = (coff & 0x3) * 8; readonly = 0xFFFFF880 >> rshift; old = CFGREAD(pi, coff, bytes); new &= ~readonly; new |= (old & readonly); CFGWRITE(pi, coff, new, bytes); /* update config */ cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ changed = cmd ^ cmd2; /* * If the MMIO or I/O address space decoding has changed then * register/unregister all BARs that decode that address space. */ for (i = 0; i <= PCI_BARMAX; i++) { switch (pi->pi_bar[i].type) { case PCIBAR_NONE: case PCIBAR_MEMHI64: break; case PCIBAR_IO: /* I/O address space decoding changed? */ if (changed & PCIM_CMD_PORTEN) { if (porten(pi)) register_bar(pi, i); else unregister_bar(pi, i); } break; case PCIBAR_MEM32: case PCIBAR_MEM64: /* MMIO address space decoding changed? */ if (changed & PCIM_CMD_MEMEN) { if (memen(pi)) register_bar(pi, i); else unregister_bar(pi, i); } break; default: assert(0); } } /* * If INTx has been unmasked and is pending, assert the * interrupt. */ pci_lintr_update(pi); } static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, int coff, int bytes, uint32_t *eax) { struct businfo *bi; struct slotinfo *si; struct pci_devinst *pi; struct pci_devemu *pe; int idx, needcfg; uint64_t addr, bar, mask; if ((bi = pci_businfo[bus]) != NULL) { si = &bi->slotinfo[slot]; pi = si->si_funcs[func].fi_devi; } else pi = NULL; /* * Just return if there is no device at this slot:func or if the * the guest is doing an un-aligned access. */ if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || (coff & (bytes - 1)) != 0) { if (in) *eax = 0xffffffff; return; } /* * Ignore all writes beyond the standard config space and return all * ones on reads. */ if (coff >= PCI_REGMAX + 1) { if (in) { *eax = 0xffffffff; /* * Extended capabilities begin at offset 256 in config * space. Absence of extended capabilities is signaled * with all 0s in the extended capability header at * offset 256. */ if (coff <= PCI_REGMAX + 4) *eax = 0x00000000; } return; } pe = pi->pi_d; /* * Config read */ if (in) { /* Let the device emulation override the default handler */ if (pe->pe_cfgread != NULL) { needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, eax); } else { needcfg = 1; } if (needcfg) *eax = CFGREAD(pi, coff, bytes); pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); } else { /* Let the device emulation override the default handler */ if (pe->pe_cfgwrite != NULL && (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) return; /* * Special handling for write to BAR registers */ if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { /* * Ignore writes to BAR registers that are not * 4-byte aligned. */ if (bytes != 4 || (coff & 0x3) != 0) return; idx = (coff - PCIR_BAR(0)) / 4; mask = ~(pi->pi_bar[idx].size - 1); switch (pi->pi_bar[idx].type) { case PCIBAR_NONE: pi->pi_bar[idx].addr = bar = 0; break; case PCIBAR_IO: addr = *eax & mask; addr &= 0xffff; bar = addr | PCIM_BAR_IO_SPACE; /* * Register the new BAR value for interception */ if (addr != pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_IO); } break; case PCIBAR_MEM32: addr = bar = *eax & mask; bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; if (addr != pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_MEM32); } break; case PCIBAR_MEM64: addr = bar = *eax & mask; bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | PCIM_BAR_MEM_PREFETCH; if (addr != (uint32_t)pi->pi_bar[idx].addr) { update_bar_address(pi, addr, idx, PCIBAR_MEM64); } break; case PCIBAR_MEMHI64: mask = ~(pi->pi_bar[idx - 1].size - 1); addr = ((uint64_t)*eax << 32) & mask; bar = addr >> 32; if (bar != pi->pi_bar[idx - 1].addr >> 32) { update_bar_address(pi, addr, idx - 1, PCIBAR_MEMHI64); } break; default: assert(0); } pci_set_cfgdata32(pi, coff, bar); } else if (pci_emul_iscap(pi, coff)) { pci_emul_capwrite(pi, coff, bytes, *eax); } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { pci_emul_cmdsts_write(pi, coff, *eax, bytes); } else { CFGWRITE(pi, coff, *eax, bytes); } } } static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; static int pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { uint32_t x; if (bytes != 4) { if (in) *eax = (bytes == 2) ? 0xffff : 0xff; return (0); } if (in) { x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; if (cfgenable) x |= CONF1_ENABLE; *eax = x; } else { x = *eax; cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; cfgoff = x & PCI_REGMAX; cfgfunc = (x >> 8) & PCI_FUNCMAX; cfgslot = (x >> 11) & PCI_SLOTMAX; cfgbus = (x >> 16) & PCI_BUSMAX; } return (0); } INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); static int pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, uint32_t *eax, void *arg) { int coff; assert(bytes == 1 || bytes == 2 || bytes == 4); coff = cfgoff + (port - CONF1_DATA_PORT); if (cfgenable) { pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax); } else { /* Ignore accesses to cfgdata if not enabled by cfgaddr */ if (in) *eax = 0xffffffff; } return (0); } INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); #define PCI_EMUL_TEST #ifdef PCI_EMUL_TEST /* * Define a dummy test device */ #define DIOSZ 8 #define DMEMSZ 4096 struct pci_emul_dsoftc { uint8_t ioregs[DIOSZ]; uint8_t memregs[2][DMEMSZ]; }; #define PCI_EMUL_MSI_MSGS 4 #define PCI_EMUL_MSIX_MSGS 16 static int pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) { int error; struct pci_emul_dsoftc *sc; sc = calloc(1, sizeof(struct pci_emul_dsoftc)); pi->pi_arg = sc; pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); assert(error == 0); error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); assert(error == 0); error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); assert(error == 0); error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); assert(error == 0); return (0); } static void pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size, uint64_t value) { int i; struct pci_emul_dsoftc *sc = pi->pi_arg; if (baridx == 0) { if (offset + size > DIOSZ) { printf("diow: iow too large, offset %ld size %d\n", offset, size); return; } if (size == 1) { sc->ioregs[offset] = value & 0xff; } else if (size == 2) { *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; } else if (size == 4) { *(uint32_t *)&sc->ioregs[offset] = value; } else { printf("diow: iow unknown size %d\n", size); } /* * Special magic value to generate an interrupt */ if (offset == 4 && size == 4 && pci_msi_enabled(pi)) pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); if (value == 0xabcdef) { for (i = 0; i < pci_msi_maxmsgnum(pi); i++) pci_generate_msi(pi, i); } } if (baridx == 1 || baridx == 2) { if (offset + size > DMEMSZ) { printf("diow: memw too large, offset %ld size %d\n", offset, size); return; } i = baridx - 1; /* 'memregs' index */ if (size == 1) { sc->memregs[i][offset] = value; } else if (size == 2) { *(uint16_t *)&sc->memregs[i][offset] = value; } else if (size == 4) { *(uint32_t *)&sc->memregs[i][offset] = value; } else if (size == 8) { *(uint64_t *)&sc->memregs[i][offset] = value; } else { printf("diow: memw unknown size %d\n", size); } /* * magic interrupt ?? */ } if (baridx > 2) { printf("diow: unknown bar idx %d\n", baridx); } } static uint64_t pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, uint64_t offset, int size) { struct pci_emul_dsoftc *sc = pi->pi_arg; uint32_t value; int i; if (baridx == 0) { if (offset + size > DIOSZ) { printf("dior: ior too large, offset %ld size %d\n", offset, size); return (0); } if (size == 1) { value = sc->ioregs[offset]; } else if (size == 2) { value = *(uint16_t *) &sc->ioregs[offset]; } else if (size == 4) { value = *(uint32_t *) &sc->ioregs[offset]; } else { printf("dior: ior unknown size %d\n", size); } } if (baridx == 1 || baridx == 2) { if (offset + size > DMEMSZ) { printf("dior: memr too large, offset %ld size %d\n", offset, size); return (0); } i = baridx - 1; /* 'memregs' index */ if (size == 1) { value = sc->memregs[i][offset]; } else if (size == 2) { value = *(uint16_t *) &sc->memregs[i][offset]; } else if (size == 4) { value = *(uint32_t *) &sc->memregs[i][offset]; } else if (size == 8) { value = *(uint64_t *) &sc->memregs[i][offset]; } else { printf("dior: ior unknown size %d\n", size); } } if (baridx > 2) { printf("dior: unknown bar idx %d\n", baridx); return (0); } return (value); } struct pci_devemu pci_dummy = { .pe_emu = "dummy", .pe_init = pci_emul_dinit, .pe_barwrite = pci_emul_diow, .pe_barread = pci_emul_dior }; PCI_EMUL_SET(pci_dummy); #endif /* PCI_EMUL_TEST */ Index: projects/clang380-import/usr.sbin/bhyve =================================================================== --- projects/clang380-import/usr.sbin/bhyve (revision 293015) +++ projects/clang380-import/usr.sbin/bhyve (revision 293016) Property changes on: projects/clang380-import/usr.sbin/bhyve ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.sbin/bhyve:r292913-293015 Index: projects/clang380-import/usr.sbin/camdd/camdd.c =================================================================== --- projects/clang380-import/usr.sbin/camdd/camdd.c (revision 293015) +++ projects/clang380-import/usr.sbin/camdd/camdd.c (revision 293016) @@ -1,3428 +1,3423 @@ /*- * Copyright (c) 1997-2007 Kenneth D. Merry * Copyright (c) 2013, 2014, 2015 Spectra Logic Corporation * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * Authors: Ken Merry (Spectra Logic Corporation) */ /* * This is eventually intended to be: * - A basic data transfer/copy utility * - A simple benchmark utility * - An example of how to use the asynchronous pass(4) driver interface. */ #include __FBSDID("$FreeBSD$"); #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { CAMDD_CMD_NONE = 0x00000000, CAMDD_CMD_HELP = 0x00000001, CAMDD_CMD_WRITE = 0x00000002, CAMDD_CMD_READ = 0x00000003 } camdd_cmdmask; typedef enum { CAMDD_ARG_NONE = 0x00000000, CAMDD_ARG_VERBOSE = 0x00000001, CAMDD_ARG_DEVICE = 0x00000002, CAMDD_ARG_BUS = 0x00000004, CAMDD_ARG_TARGET = 0x00000008, CAMDD_ARG_LUN = 0x00000010, CAMDD_ARG_UNIT = 0x00000020, CAMDD_ARG_TIMEOUT = 0x00000040, CAMDD_ARG_ERR_RECOVER = 0x00000080, CAMDD_ARG_RETRIES = 0x00000100 } camdd_argmask; typedef enum { CAMDD_DEV_NONE = 0x00, CAMDD_DEV_PASS = 0x01, CAMDD_DEV_FILE = 0x02 } camdd_dev_type; struct camdd_io_opts { camdd_dev_type dev_type; char *dev_name; uint64_t blocksize; uint64_t queue_depth; uint64_t offset; int min_cmd_size; int write_dev; uint64_t debug; }; typedef enum { CAMDD_BUF_NONE, CAMDD_BUF_DATA, CAMDD_BUF_INDIRECT } camdd_buf_type; struct camdd_buf_indirect { /* * Pointer to the source buffer. */ struct camdd_buf *src_buf; /* * Offset into the source buffer, in bytes. */ uint64_t offset; /* * Pointer to the starting point in the source buffer. */ uint8_t *start_ptr; /* * Length of this chunk in bytes. */ size_t len; }; struct camdd_buf_data { /* * Buffer allocated when we allocate this camdd_buf. This should * be the size of the blocksize for this device. */ uint8_t *buf; /* * The amount of backing store allocated in buf. Generally this * will be the blocksize of the device. */ uint32_t alloc_len; /* * The amount of data that was put into the buffer (on reads) or * the amount of data we have put onto the src_list so far (on * writes). */ uint32_t fill_len; /* * The amount of data that was not transferred. */ uint32_t resid; /* * Starting byte offset on the reader. */ uint64_t src_start_offset; /* * CCB used for pass(4) device targets. */ union ccb ccb; /* * Number of scatter/gather segments. */ int sg_count; /* * Set if we had to tack on an extra buffer to round the transfer * up to a sector size. */ int extra_buf; /* * Scatter/gather list used generally when we're the writer for a * pass(4) device. */ bus_dma_segment_t *segs; /* * Scatter/gather list used generally when we're the writer for a * file or block device; */ struct iovec *iovec; }; union camdd_buf_types { struct camdd_buf_indirect indirect; struct camdd_buf_data data; }; typedef enum { CAMDD_STATUS_NONE, CAMDD_STATUS_OK, CAMDD_STATUS_SHORT_IO, CAMDD_STATUS_EOF, CAMDD_STATUS_ERROR } camdd_buf_status; struct camdd_buf { camdd_buf_type buf_type; union camdd_buf_types buf_type_spec; camdd_buf_status status; uint64_t lba; size_t len; /* * A reference count of how many indirect buffers point to this * buffer. */ int refcount; /* * A link back to our parent device. */ struct camdd_dev *dev; STAILQ_ENTRY(camdd_buf) links; STAILQ_ENTRY(camdd_buf) work_links; /* * A count of the buffers on the src_list. */ int src_count; /* * List of buffers from our partner thread that are the components * of this buffer for the I/O. Uses src_links. */ STAILQ_HEAD(,camdd_buf) src_list; STAILQ_ENTRY(camdd_buf) src_links; }; #define NUM_DEV_TYPES 2 struct camdd_dev_pass { int scsi_dev_type; struct cam_device *dev; uint64_t max_sector; uint32_t block_len; uint32_t cpi_maxio; }; typedef enum { CAMDD_FILE_NONE, CAMDD_FILE_REG, CAMDD_FILE_STD, CAMDD_FILE_PIPE, CAMDD_FILE_DISK, CAMDD_FILE_TAPE, CAMDD_FILE_TTY, CAMDD_FILE_MEM } camdd_file_type; typedef enum { CAMDD_FF_NONE = 0x00, CAMDD_FF_CAN_SEEK = 0x01 } camdd_file_flags; struct camdd_dev_file { int fd; struct stat sb; char filename[MAXPATHLEN + 1]; camdd_file_type file_type; camdd_file_flags file_flags; uint8_t *tmp_buf; }; struct camdd_dev_block { int fd; uint64_t size_bytes; uint32_t block_len; }; union camdd_dev_spec { struct camdd_dev_pass pass; struct camdd_dev_file file; struct camdd_dev_block block; }; typedef enum { CAMDD_DEV_FLAG_NONE = 0x00, CAMDD_DEV_FLAG_EOF = 0x01, CAMDD_DEV_FLAG_PEER_EOF = 0x02, CAMDD_DEV_FLAG_ACTIVE = 0x04, CAMDD_DEV_FLAG_EOF_SENT = 0x08, CAMDD_DEV_FLAG_EOF_QUEUED = 0x10 } camdd_dev_flags; struct camdd_dev { camdd_dev_type dev_type; union camdd_dev_spec dev_spec; camdd_dev_flags flags; char device_name[MAXPATHLEN+1]; uint32_t blocksize; uint32_t sector_size; uint64_t max_sector; uint64_t sector_io_limit; int min_cmd_size; int write_dev; int retry_count; int io_timeout; int debug; uint64_t start_offset_bytes; uint64_t next_io_pos_bytes; uint64_t next_peer_pos_bytes; uint64_t next_completion_pos_bytes; uint64_t peer_bytes_queued; uint64_t bytes_transferred; uint32_t target_queue_depth; uint32_t cur_active_io; uint8_t *extra_buf; uint32_t extra_buf_len; struct camdd_dev *peer_dev; pthread_mutex_t mutex; pthread_cond_t cond; int kq; int (*run)(struct camdd_dev *dev); int (*fetch)(struct camdd_dev *dev); /* * Buffers that are available for I/O. Uses links. */ STAILQ_HEAD(,camdd_buf) free_queue; /* * Free indirect buffers. These are used for breaking a large * buffer into multiple pieces. */ STAILQ_HEAD(,camdd_buf) free_indirect_queue; /* * Buffers that have been queued to the kernel. Uses links. */ STAILQ_HEAD(,camdd_buf) active_queue; /* * Will generally contain one of our buffers that is waiting for enough * I/O from our partner thread to be able to execute. This will * generally happen when our per-I/O-size is larger than the * partner thread's per-I/O-size. Uses links. */ STAILQ_HEAD(,camdd_buf) pending_queue; /* * Number of buffers on the pending queue */ int num_pending_queue; /* * Buffers that are filled and ready to execute. This is used when * our partner (reader) thread sends us blocks that are larger than * our blocksize, and so we have to split them into multiple pieces. */ STAILQ_HEAD(,camdd_buf) run_queue; /* * Number of buffers on the run queue. */ int num_run_queue; STAILQ_HEAD(,camdd_buf) reorder_queue; int num_reorder_queue; /* * Buffers that have been queued to us by our partner thread * (generally the reader thread) to be written out. Uses * work_links. */ STAILQ_HEAD(,camdd_buf) work_queue; /* * Buffers that have been completed by our partner thread. Uses * work_links. */ STAILQ_HEAD(,camdd_buf) peer_done_queue; /* * Number of buffers on the peer done queue. */ uint32_t num_peer_done_queue; /* * A list of buffers that we have queued to our peer thread. Uses * links. */ STAILQ_HEAD(,camdd_buf) peer_work_queue; /* * Number of buffers on the peer work queue. */ uint32_t num_peer_work_queue; }; static sem_t camdd_sem; static int need_exit = 0; static int error_exit = 0; static int need_status = 0; #ifndef min #define min(a, b) (a < b) ? a : b #endif /* * XXX KDM private copy of timespecsub(). This is normally defined in * sys/time.h, but is only enabled in the kernel. If that definition is * enabled in userland, it breaks the build of libnetbsd. */ #ifndef timespecsub #define timespecsub(vvp, uvp) \ do { \ (vvp)->tv_sec -= (uvp)->tv_sec; \ (vvp)->tv_nsec -= (uvp)->tv_nsec; \ if ((vvp)->tv_nsec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_nsec += 1000000000; \ } \ } while (0) #endif /* Generically usefull offsets into the peripheral private area */ #define ppriv_ptr0 periph_priv.entries[0].ptr #define ppriv_ptr1 periph_priv.entries[1].ptr #define ppriv_field0 periph_priv.entries[0].field #define ppriv_field1 periph_priv.entries[1].field #define ccb_buf ppriv_ptr0 #define CAMDD_FILE_DEFAULT_BLOCK 524288 #define CAMDD_FILE_DEFAULT_DEPTH 1 #define CAMDD_PASS_MAX_BLOCK 1048576 #define CAMDD_PASS_DEFAULT_DEPTH 6 #define CAMDD_PASS_RW_TIMEOUT 60 * 1000 static int parse_btl(char *tstr, int *bus, int *target, int *lun, camdd_argmask *arglst); void camdd_free_dev(struct camdd_dev *dev); struct camdd_dev *camdd_alloc_dev(camdd_dev_type dev_type, struct kevent *new_ke, int num_ke, int retry_count, int timeout); static struct camdd_buf *camdd_alloc_buf(struct camdd_dev *dev, camdd_buf_type buf_type); void camdd_release_buf(struct camdd_buf *buf); struct camdd_buf *camdd_get_buf(struct camdd_dev *dev, camdd_buf_type buf_type); int camdd_buf_sg_create(struct camdd_buf *buf, int iovec, uint32_t sector_size, uint32_t *num_sectors_used, int *double_buf_needed); uint32_t camdd_buf_get_len(struct camdd_buf *buf); void camdd_buf_add_child(struct camdd_buf *buf, struct camdd_buf *child_buf); int camdd_probe_tape(int fd, char *filename, uint64_t *max_iosize, uint64_t *max_blk, uint64_t *min_blk, uint64_t *blk_gran); struct camdd_dev *camdd_probe_file(int fd, struct camdd_io_opts *io_opts, int retry_count, int timeout); struct camdd_dev *camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts, camdd_argmask arglist, int probe_retry_count, int probe_timeout, int io_retry_count, int io_timeout); void *camdd_file_worker(void *arg); camdd_buf_status camdd_ccb_status(union ccb *ccb); int camdd_queue_peer_buf(struct camdd_dev *dev, struct camdd_buf *buf); int camdd_complete_peer_buf(struct camdd_dev *dev, struct camdd_buf *peer_buf); void camdd_peer_done(struct camdd_buf *buf); void camdd_complete_buf(struct camdd_dev *dev, struct camdd_buf *buf, int *error_count); int camdd_pass_fetch(struct camdd_dev *dev); int camdd_file_run(struct camdd_dev *dev); int camdd_pass_run(struct camdd_dev *dev); int camdd_get_next_lba_len(struct camdd_dev *dev, uint64_t *lba, ssize_t *len); int camdd_queue(struct camdd_dev *dev, struct camdd_buf *read_buf); void camdd_get_depth(struct camdd_dev *dev, uint32_t *our_depth, uint32_t *peer_depth, uint32_t *our_bytes, uint32_t *peer_bytes); void *camdd_worker(void *arg); void camdd_sig_handler(int sig); void camdd_print_status(struct camdd_dev *camdd_dev, struct camdd_dev *other_dev, struct timespec *start_time); int camdd_rw(struct camdd_io_opts *io_opts, int num_io_opts, uint64_t max_io, int retry_count, int timeout); int camdd_parse_io_opts(char *args, int is_write, struct camdd_io_opts *io_opts); void usage(void); /* * Parse out a bus, or a bus, target and lun in the following * format: * bus * bus:target * bus:target:lun * * Returns the number of parsed components, or 0. */ static int parse_btl(char *tstr, int *bus, int *target, int *lun, camdd_argmask *arglst) { char *tmpstr; int convs = 0; while (isspace(*tstr) && (*tstr != '\0')) tstr++; tmpstr = (char *)strtok(tstr, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *bus = strtol(tmpstr, NULL, 0); *arglst |= CAMDD_ARG_BUS; convs++; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *target = strtol(tmpstr, NULL, 0); *arglst |= CAMDD_ARG_TARGET; convs++; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *lun = strtol(tmpstr, NULL, 0); *arglst |= CAMDD_ARG_LUN; convs++; } } } return convs; } /* * XXX KDM clean up and free all of the buffers on the queue! */ void camdd_free_dev(struct camdd_dev *dev) { if (dev == NULL) return; switch (dev->dev_type) { case CAMDD_DEV_FILE: { struct camdd_dev_file *file_dev = &dev->dev_spec.file; if (file_dev->fd != -1) close(file_dev->fd); free(file_dev->tmp_buf); break; } case CAMDD_DEV_PASS: { struct camdd_dev_pass *pass_dev = &dev->dev_spec.pass; if (pass_dev->dev != NULL) cam_close_device(pass_dev->dev); break; } default: break; } free(dev); } struct camdd_dev * camdd_alloc_dev(camdd_dev_type dev_type, struct kevent *new_ke, int num_ke, int retry_count, int timeout) { struct camdd_dev *dev = NULL; struct kevent *ke; size_t ke_size; int retval = 0; dev = malloc(sizeof(*dev)); if (dev == NULL) { warn("%s: unable to malloc %zu bytes", __func__, sizeof(*dev)); goto bailout; } bzero(dev, sizeof(*dev)); dev->dev_type = dev_type; dev->io_timeout = timeout; dev->retry_count = retry_count; STAILQ_INIT(&dev->free_queue); STAILQ_INIT(&dev->free_indirect_queue); STAILQ_INIT(&dev->active_queue); STAILQ_INIT(&dev->pending_queue); STAILQ_INIT(&dev->run_queue); STAILQ_INIT(&dev->reorder_queue); STAILQ_INIT(&dev->work_queue); STAILQ_INIT(&dev->peer_done_queue); STAILQ_INIT(&dev->peer_work_queue); retval = pthread_mutex_init(&dev->mutex, NULL); if (retval != 0) { warnc(retval, "%s: failed to initialize mutex", __func__); goto bailout; } retval = pthread_cond_init(&dev->cond, NULL); if (retval != 0) { warnc(retval, "%s: failed to initialize condition variable", __func__); goto bailout; } dev->kq = kqueue(); if (dev->kq == -1) { warn("%s: Unable to create kqueue", __func__); goto bailout; } ke_size = sizeof(struct kevent) * (num_ke + 4); ke = malloc(ke_size); if (ke == NULL) { warn("%s: unable to malloc %zu bytes", __func__, ke_size); goto bailout; } bzero(ke, ke_size); if (num_ke > 0) bcopy(new_ke, ke, num_ke * sizeof(struct kevent)); EV_SET(&ke[num_ke++], (uintptr_t)&dev->work_queue, EVFILT_USER, EV_ADD|EV_ENABLE|EV_CLEAR, 0,0, 0); EV_SET(&ke[num_ke++], (uintptr_t)&dev->peer_done_queue, EVFILT_USER, EV_ADD|EV_ENABLE|EV_CLEAR, 0,0, 0); EV_SET(&ke[num_ke++], SIGINFO, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0,0,0); EV_SET(&ke[num_ke++], SIGINT, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0,0,0); retval = kevent(dev->kq, ke, num_ke, NULL, 0, NULL); if (retval == -1) { warn("%s: Unable to register kevents", __func__); goto bailout; } return (dev); bailout: free(dev); return (NULL); } static struct camdd_buf * camdd_alloc_buf(struct camdd_dev *dev, camdd_buf_type buf_type) { struct camdd_buf *buf = NULL; uint8_t *data_ptr = NULL; /* * We only need to allocate data space for data buffers. */ switch (buf_type) { case CAMDD_BUF_DATA: data_ptr = malloc(dev->blocksize); if (data_ptr == NULL) { warn("unable to allocate %u bytes", dev->blocksize); goto bailout_error; } break; default: break; } buf = malloc(sizeof(*buf)); if (buf == NULL) { warn("unable to allocate %zu bytes", sizeof(*buf)); goto bailout_error; } bzero(buf, sizeof(*buf)); buf->buf_type = buf_type; buf->dev = dev; switch (buf_type) { case CAMDD_BUF_DATA: { struct camdd_buf_data *data; data = &buf->buf_type_spec.data; data->alloc_len = dev->blocksize; data->buf = data_ptr; break; } case CAMDD_BUF_INDIRECT: break; default: break; } STAILQ_INIT(&buf->src_list); return (buf); bailout_error: if (data_ptr != NULL) free(data_ptr); if (buf != NULL) free(buf); return (NULL); } void camdd_release_buf(struct camdd_buf *buf) { struct camdd_dev *dev; dev = buf->dev; switch (buf->buf_type) { case CAMDD_BUF_DATA: { struct camdd_buf_data *data; data = &buf->buf_type_spec.data; if (data->segs != NULL) { if (data->extra_buf != 0) { void *extra_buf; extra_buf = (void *) data->segs[data->sg_count - 1].ds_addr; free(extra_buf); data->extra_buf = 0; } free(data->segs); data->segs = NULL; data->sg_count = 0; } else if (data->iovec != NULL) { if (data->extra_buf != 0) { free(data->iovec[data->sg_count - 1].iov_base); data->extra_buf = 0; } free(data->iovec); data->iovec = NULL; data->sg_count = 0; } STAILQ_INSERT_TAIL(&dev->free_queue, buf, links); break; } case CAMDD_BUF_INDIRECT: STAILQ_INSERT_TAIL(&dev->free_indirect_queue, buf, links); break; default: err(1, "%s: Invalid buffer type %d for released buffer", __func__, buf->buf_type); break; } } struct camdd_buf * camdd_get_buf(struct camdd_dev *dev, camdd_buf_type buf_type) { struct camdd_buf *buf = NULL; switch (buf_type) { case CAMDD_BUF_DATA: buf = STAILQ_FIRST(&dev->free_queue); if (buf != NULL) { struct camdd_buf_data *data; uint8_t *data_ptr; uint32_t alloc_len; STAILQ_REMOVE_HEAD(&dev->free_queue, links); data = &buf->buf_type_spec.data; data_ptr = data->buf; alloc_len = data->alloc_len; bzero(buf, sizeof(*buf)); data->buf = data_ptr; data->alloc_len = alloc_len; } break; case CAMDD_BUF_INDIRECT: buf = STAILQ_FIRST(&dev->free_indirect_queue); if (buf != NULL) { STAILQ_REMOVE_HEAD(&dev->free_indirect_queue, links); bzero(buf, sizeof(*buf)); } break; default: warnx("Unknown buffer type %d requested", buf_type); break; } if (buf == NULL) return (camdd_alloc_buf(dev, buf_type)); else { STAILQ_INIT(&buf->src_list); buf->dev = dev; buf->buf_type = buf_type; return (buf); } } int camdd_buf_sg_create(struct camdd_buf *buf, int iovec, uint32_t sector_size, uint32_t *num_sectors_used, int *double_buf_needed) { struct camdd_buf *tmp_buf; struct camdd_buf_data *data; uint8_t *extra_buf = NULL; size_t extra_buf_len = 0; int i, retval = 0; data = &buf->buf_type_spec.data; data->sg_count = buf->src_count; /* * Compose a scatter/gather list from all of the buffers in the list. * If the length of the buffer isn't a multiple of the sector size, * we'll have to add an extra buffer. This should only happen * at the end of a transfer. */ if ((data->fill_len % sector_size) != 0) { extra_buf_len = sector_size - (data->fill_len % sector_size); extra_buf = calloc(extra_buf_len, 1); if (extra_buf == NULL) { warn("%s: unable to allocate %zu bytes for extra " "buffer space", __func__, extra_buf_len); retval = 1; goto bailout; } data->extra_buf = 1; data->sg_count++; } if (iovec == 0) { data->segs = calloc(data->sg_count, sizeof(bus_dma_segment_t)); if (data->segs == NULL) { warn("%s: unable to allocate %zu bytes for S/G list", __func__, sizeof(bus_dma_segment_t) * data->sg_count); retval = 1; goto bailout; } } else { data->iovec = calloc(data->sg_count, sizeof(struct iovec)); if (data->iovec == NULL) { warn("%s: unable to allocate %zu bytes for S/G list", __func__, sizeof(struct iovec) * data->sg_count); retval = 1; goto bailout; } } for (i = 0, tmp_buf = STAILQ_FIRST(&buf->src_list); i < buf->src_count && tmp_buf != NULL; i++, tmp_buf = STAILQ_NEXT(tmp_buf, src_links)) { if (tmp_buf->buf_type == CAMDD_BUF_DATA) { struct camdd_buf_data *tmp_data; tmp_data = &tmp_buf->buf_type_spec.data; if (iovec == 0) { data->segs[i].ds_addr = (bus_addr_t) tmp_data->buf; data->segs[i].ds_len = tmp_data->fill_len - tmp_data->resid; } else { data->iovec[i].iov_base = tmp_data->buf; data->iovec[i].iov_len = tmp_data->fill_len - tmp_data->resid; } if (((tmp_data->fill_len - tmp_data->resid) % sector_size) != 0) *double_buf_needed = 1; } else { struct camdd_buf_indirect *tmp_ind; tmp_ind = &tmp_buf->buf_type_spec.indirect; if (iovec == 0) { data->segs[i].ds_addr = (bus_addr_t)tmp_ind->start_ptr; data->segs[i].ds_len = tmp_ind->len; } else { data->iovec[i].iov_base = tmp_ind->start_ptr; data->iovec[i].iov_len = tmp_ind->len; } if ((tmp_ind->len % sector_size) != 0) *double_buf_needed = 1; } } if (extra_buf != NULL) { if (iovec == 0) { data->segs[i].ds_addr = (bus_addr_t)extra_buf; data->segs[i].ds_len = extra_buf_len; } else { data->iovec[i].iov_base = extra_buf; data->iovec[i].iov_len = extra_buf_len; } i++; } if ((tmp_buf != NULL) || (i != data->sg_count)) { warnx("buffer source count does not match " "number of buffers in list!"); retval = 1; goto bailout; } bailout: if (retval == 0) { *num_sectors_used = (data->fill_len + extra_buf_len) / sector_size; } return (retval); } uint32_t camdd_buf_get_len(struct camdd_buf *buf) { uint32_t len = 0; if (buf->buf_type != CAMDD_BUF_DATA) { struct camdd_buf_indirect *indirect; indirect = &buf->buf_type_spec.indirect; len = indirect->len; } else { struct camdd_buf_data *data; data = &buf->buf_type_spec.data; len = data->fill_len; } return (len); } void camdd_buf_add_child(struct camdd_buf *buf, struct camdd_buf *child_buf) { struct camdd_buf_data *data; assert(buf->buf_type == CAMDD_BUF_DATA); data = &buf->buf_type_spec.data; STAILQ_INSERT_TAIL(&buf->src_list, child_buf, src_links); buf->src_count++; data->fill_len += camdd_buf_get_len(child_buf); } typedef enum { CAMDD_TS_MAX_BLK, CAMDD_TS_MIN_BLK, CAMDD_TS_BLK_GRAN, CAMDD_TS_EFF_IOSIZE } camdd_status_item_index; static struct camdd_status_items { const char *name; struct mt_status_entry *entry; } req_status_items[] = { { "max_blk", NULL }, { "min_blk", NULL }, { "blk_gran", NULL }, { "max_effective_iosize", NULL } }; int camdd_probe_tape(int fd, char *filename, uint64_t *max_iosize, uint64_t *max_blk, uint64_t *min_blk, uint64_t *blk_gran) { struct mt_status_data status_data; char *xml_str = NULL; unsigned int i; int retval = 0; retval = mt_get_xml_str(fd, MTIOCEXTGET, &xml_str); if (retval != 0) err(1, "Couldn't get XML string from %s", filename); retval = mt_get_status(xml_str, &status_data); if (retval != XML_STATUS_OK) { warn("couldn't get status for %s", filename); retval = 1; goto bailout; } else retval = 0; if (status_data.error != 0) { warnx("%s", status_data.error_str); retval = 1; goto bailout; } for (i = 0; i < sizeof(req_status_items) / sizeof(req_status_items[0]); i++) { char *name; name = __DECONST(char *, req_status_items[i].name); req_status_items[i].entry = mt_status_entry_find(&status_data, name); if (req_status_items[i].entry == NULL) { errx(1, "Cannot find status entry %s", req_status_items[i].name); } } *max_iosize = req_status_items[CAMDD_TS_EFF_IOSIZE].entry->value_unsigned; *max_blk= req_status_items[CAMDD_TS_MAX_BLK].entry->value_unsigned; *min_blk= req_status_items[CAMDD_TS_MIN_BLK].entry->value_unsigned; *blk_gran = req_status_items[CAMDD_TS_BLK_GRAN].entry->value_unsigned; bailout: free(xml_str); mt_status_free(&status_data); return (retval); } struct camdd_dev * camdd_probe_file(int fd, struct camdd_io_opts *io_opts, int retry_count, int timeout) { struct camdd_dev *dev = NULL; struct camdd_dev_file *file_dev; uint64_t blocksize = io_opts->blocksize; dev = camdd_alloc_dev(CAMDD_DEV_FILE, NULL, 0, retry_count, timeout); if (dev == NULL) goto bailout; file_dev = &dev->dev_spec.file; file_dev->fd = fd; strlcpy(file_dev->filename, io_opts->dev_name, sizeof(file_dev->filename)); strlcpy(dev->device_name, io_opts->dev_name, sizeof(dev->device_name)); if (blocksize == 0) dev->blocksize = CAMDD_FILE_DEFAULT_BLOCK; else dev->blocksize = blocksize; if ((io_opts->queue_depth != 0) && (io_opts->queue_depth != 1)) { warnx("Queue depth %ju for %s ignored, only 1 outstanding " "command supported", (uintmax_t)io_opts->queue_depth, io_opts->dev_name); } dev->target_queue_depth = CAMDD_FILE_DEFAULT_DEPTH; dev->run = camdd_file_run; dev->fetch = NULL; /* * We can effectively access files on byte boundaries. We'll reset * this for devices like disks that can be accessed on sector * boundaries. */ dev->sector_size = 1; if ((fd != STDIN_FILENO) && (fd != STDOUT_FILENO)) { int retval; retval = fstat(fd, &file_dev->sb); if (retval != 0) { warn("Cannot stat %s", dev->device_name); goto bailout; camdd_free_dev(dev); dev = NULL; } if (S_ISREG(file_dev->sb.st_mode)) { file_dev->file_type = CAMDD_FILE_REG; } else if (S_ISCHR(file_dev->sb.st_mode)) { int type; if (ioctl(fd, FIODTYPE, &type) == -1) err(1, "FIODTYPE ioctl failed on %s", dev->device_name); else { if (type & D_TAPE) file_dev->file_type = CAMDD_FILE_TAPE; else if (type & D_DISK) file_dev->file_type = CAMDD_FILE_DISK; else if (type & D_MEM) file_dev->file_type = CAMDD_FILE_MEM; else if (type & D_TTY) file_dev->file_type = CAMDD_FILE_TTY; } } else if (S_ISDIR(file_dev->sb.st_mode)) { errx(1, "cannot operate on directory %s", dev->device_name); } else if (S_ISFIFO(file_dev->sb.st_mode)) { file_dev->file_type = CAMDD_FILE_PIPE; } else errx(1, "Cannot determine file type for %s", dev->device_name); switch (file_dev->file_type) { case CAMDD_FILE_REG: if (file_dev->sb.st_size != 0) dev->max_sector = file_dev->sb.st_size - 1; else dev->max_sector = 0; file_dev->file_flags |= CAMDD_FF_CAN_SEEK; break; case CAMDD_FILE_TAPE: { uint64_t max_iosize, max_blk, min_blk, blk_gran; /* * Check block limits and maximum effective iosize. * Make sure the blocksize is within the block * limits (and a multiple of the minimum blocksize) * and that the blocksize is <= maximum effective * iosize. */ retval = camdd_probe_tape(fd, dev->device_name, &max_iosize, &max_blk, &min_blk, &blk_gran); if (retval != 0) errx(1, "Unable to probe tape %s", dev->device_name); /* * The blocksize needs to be <= the maximum * effective I/O size of the tape device. Note * that this also takes into account the maximum * blocksize reported by READ BLOCK LIMITS. */ if (dev->blocksize > max_iosize) { warnx("Blocksize %u too big for %s, limiting " "to %ju", dev->blocksize, dev->device_name, max_iosize); dev->blocksize = max_iosize; } /* * The blocksize needs to be at least min_blk; */ if (dev->blocksize < min_blk) { warnx("Blocksize %u too small for %s, " "increasing to %ju", dev->blocksize, dev->device_name, min_blk); dev->blocksize = min_blk; } /* * And the blocksize needs to be a multiple of * the block granularity. */ if ((blk_gran != 0) && (dev->blocksize % (1 << blk_gran))) { warnx("Blocksize %u for %s not a multiple of " "%d, adjusting to %d", dev->blocksize, dev->device_name, (1 << blk_gran), dev->blocksize & ~((1 << blk_gran) - 1)); dev->blocksize &= ~((1 << blk_gran) - 1); } if (dev->blocksize == 0) { errx(1, "Unable to derive valid blocksize for " "%s", dev->device_name); } /* * For tape drives, set the sector size to the * blocksize so that we make sure not to write * less than the blocksize out to the drive. */ dev->sector_size = dev->blocksize; break; } case CAMDD_FILE_DISK: { off_t media_size; unsigned int sector_size; file_dev->file_flags |= CAMDD_FF_CAN_SEEK; if (ioctl(fd, DIOCGSECTORSIZE, §or_size) == -1) { err(1, "DIOCGSECTORSIZE ioctl failed on %s", dev->device_name); } if (sector_size == 0) { errx(1, "DIOCGSECTORSIZE ioctl returned " "invalid sector size %u for %s", sector_size, dev->device_name); } if (ioctl(fd, DIOCGMEDIASIZE, &media_size) == -1) { err(1, "DIOCGMEDIASIZE ioctl failed on %s", dev->device_name); } if (media_size == 0) { errx(1, "DIOCGMEDIASIZE ioctl returned " "invalid media size %ju for %s", (uintmax_t)media_size, dev->device_name); } if (dev->blocksize % sector_size) { errx(1, "%s blocksize %u not a multiple of " "sector size %u", dev->device_name, dev->blocksize, sector_size); } dev->sector_size = sector_size; dev->max_sector = (media_size / sector_size) - 1; break; } case CAMDD_FILE_MEM: file_dev->file_flags |= CAMDD_FF_CAN_SEEK; break; default: break; } } if ((io_opts->offset != 0) && ((file_dev->file_flags & CAMDD_FF_CAN_SEEK) == 0)) { warnx("Offset %ju specified for %s, but we cannot seek on %s", io_opts->offset, io_opts->dev_name, io_opts->dev_name); goto bailout_error; } #if 0 else if ((io_opts->offset != 0) && ((io_opts->offset % dev->sector_size) != 0)) { warnx("Offset %ju for %s is not a multiple of the " "sector size %u", io_opts->offset, io_opts->dev_name, dev->sector_size); goto bailout_error; } else { dev->start_offset_bytes = io_opts->offset; } #endif bailout: return (dev); bailout_error: camdd_free_dev(dev); return (NULL); } /* * Need to implement this. Do a basic probe: * - Check the inquiry data, make sure we're talking to a device that we * can reasonably expect to talk to -- direct, RBC, CD, WORM. * - Send a test unit ready, make sure the device is available. * - Get the capacity and block size. */ struct camdd_dev * camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts, camdd_argmask arglist, int probe_retry_count, int probe_timeout, int io_retry_count, int io_timeout) { union ccb *ccb; uint64_t maxsector; uint32_t cpi_maxio, max_iosize, pass_numblocks; uint32_t block_len; struct scsi_read_capacity_data rcap; struct scsi_read_capacity_data_long rcaplong; struct camdd_dev *dev; struct camdd_dev_pass *pass_dev; struct kevent ke; int scsi_dev_type; - int retval; dev = NULL; scsi_dev_type = SID_TYPE(&cam_dev->inq_data); maxsector = 0; block_len = 0; /* * For devices that support READ CAPACITY, we'll attempt to get the * capacity. Otherwise, we really don't support tape or other * devices via SCSI passthrough, so just return an error in that case. */ switch (scsi_dev_type) { case T_DIRECT: case T_WORM: case T_CDROM: case T_OPTICAL: case T_RBC: break; default: errx(1, "Unsupported SCSI device type %d", scsi_dev_type); break; /*NOTREACHED*/ } ccb = cam_getccb(cam_dev); if (ccb == NULL) { warnx("%s: error allocating ccb", __func__); goto bailout; } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); scsi_read_capacity(&ccb->csio, /*retries*/ probe_retry_count, /*cbfcnp*/ NULL, /*tag_action*/ MSG_SIMPLE_Q_TAG, &rcap, SSD_FULL_SIZE, /*timeout*/ probe_timeout ? probe_timeout : 5000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAMDD_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(cam_dev, ccb) < 0) { warn("error sending READ CAPACITY command"); cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); - retval = 1; goto bailout; } maxsector = scsi_4btoul(rcap.addr); block_len = scsi_4btoul(rcap.length); /* * A last block of 2^32-1 means that the true capacity is over 2TB, * and we need to issue the long READ CAPACITY to get the real * capacity. Otherwise, we're all set. */ if (maxsector != 0xffffffff) goto rcap_done; scsi_read_capacity_16(&ccb->csio, /*retries*/ probe_retry_count, /*cbfcnp*/ NULL, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*lba*/ 0, /*reladdr*/ 0, /*pmi*/ 0, (uint8_t *)&rcaplong, sizeof(rcaplong), /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ probe_timeout ? probe_timeout : 5000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAMDD_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(cam_dev, ccb) < 0) { warn("error sending READ CAPACITY (16) command"); - cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); - - retval = 1; goto bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } maxsector = scsi_8btou64(rcaplong.addr); block_len = scsi_4btoul(rcaplong.length); rcap_done: bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_PATH_INQ; ccb->ccb_h.flags = CAM_DIR_NONE; ccb->ccb_h.retry_count = 1; if (cam_send_ccb(cam_dev, ccb) < 0) { warn("error sending XPT_PATH_INQ CCB"); cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } EV_SET(&ke, cam_dev->fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); dev = camdd_alloc_dev(CAMDD_DEV_PASS, &ke, 1, io_retry_count, io_timeout); if (dev == NULL) goto bailout; pass_dev = &dev->dev_spec.pass; pass_dev->scsi_dev_type = scsi_dev_type; pass_dev->dev = cam_dev; pass_dev->max_sector = maxsector; pass_dev->block_len = block_len; pass_dev->cpi_maxio = ccb->cpi.maxio; snprintf(dev->device_name, sizeof(dev->device_name), "%s%u", pass_dev->dev->device_name, pass_dev->dev->dev_unit_num); dev->sector_size = block_len; dev->max_sector = maxsector; /* * Determine the optimal blocksize to use for this device. */ /* * If the controller has not specified a maximum I/O size, * just go with 128K as a somewhat conservative value. */ if (pass_dev->cpi_maxio == 0) cpi_maxio = 131072; else cpi_maxio = pass_dev->cpi_maxio; /* * If the controller has a large maximum I/O size, limit it * to something smaller so that the kernel doesn't have trouble * allocating buffers to copy data in and out for us. * XXX KDM this is until we have unmapped I/O support in the kernel. */ max_iosize = min(cpi_maxio, CAMDD_PASS_MAX_BLOCK); /* * If we weren't able to get a block size for some reason, * default to 512 bytes. */ block_len = pass_dev->block_len; if (block_len == 0) block_len = 512; /* * Figure out how many blocksize chunks will fit in the * maximum I/O size. */ pass_numblocks = max_iosize / block_len; /* * And finally, multiple the number of blocks by the LBA * length to get our maximum block size; */ dev->blocksize = pass_numblocks * block_len; if (io_opts->blocksize != 0) { if ((io_opts->blocksize % dev->sector_size) != 0) { warnx("Blocksize %ju for %s is not a multiple of " "sector size %u", (uintmax_t)io_opts->blocksize, dev->device_name, dev->sector_size); goto bailout_error; } dev->blocksize = io_opts->blocksize; } dev->target_queue_depth = CAMDD_PASS_DEFAULT_DEPTH; if (io_opts->queue_depth != 0) dev->target_queue_depth = io_opts->queue_depth; if (io_opts->offset != 0) { if (io_opts->offset > (dev->max_sector * dev->sector_size)) { warnx("Offset %ju is past the end of device %s", io_opts->offset, dev->device_name); goto bailout_error; } #if 0 else if ((io_opts->offset % dev->sector_size) != 0) { warnx("Offset %ju for %s is not a multiple of the " "sector size %u", io_opts->offset, dev->device_name, dev->sector_size); goto bailout_error; } dev->start_offset_bytes = io_opts->offset; #endif } dev->min_cmd_size = io_opts->min_cmd_size; dev->run = camdd_pass_run; dev->fetch = camdd_pass_fetch; bailout: cam_freeccb(ccb); return (dev); bailout_error: cam_freeccb(ccb); camdd_free_dev(dev); return (NULL); } void * camdd_worker(void *arg) { struct camdd_dev *dev = arg; struct camdd_buf *buf; struct timespec ts, *kq_ts; ts.tv_sec = 0; ts.tv_nsec = 0; pthread_mutex_lock(&dev->mutex); dev->flags |= CAMDD_DEV_FLAG_ACTIVE; for (;;) { struct kevent ke; int retval = 0; /* * XXX KDM check the reorder queue depth? */ if (dev->write_dev == 0) { uint32_t our_depth, peer_depth, peer_bytes, our_bytes; uint32_t target_depth = dev->target_queue_depth; uint32_t peer_target_depth = dev->peer_dev->target_queue_depth; uint32_t peer_blocksize = dev->peer_dev->blocksize; camdd_get_depth(dev, &our_depth, &peer_depth, &our_bytes, &peer_bytes); #if 0 while (((our_depth < target_depth) && (peer_depth < peer_target_depth)) || ((peer_bytes + our_bytes) < (peer_blocksize * 2))) { #endif while (((our_depth + peer_depth) < (target_depth + peer_target_depth)) || ((peer_bytes + our_bytes) < (peer_blocksize * 3))) { retval = camdd_queue(dev, NULL); if (retval == 1) break; else if (retval != 0) { error_exit = 1; goto bailout; } camdd_get_depth(dev, &our_depth, &peer_depth, &our_bytes, &peer_bytes); } } /* * See if we have any I/O that is ready to execute. */ buf = STAILQ_FIRST(&dev->run_queue); if (buf != NULL) { while (dev->target_queue_depth > dev->cur_active_io) { retval = dev->run(dev); if (retval == -1) { dev->flags |= CAMDD_DEV_FLAG_EOF; error_exit = 1; break; } else if (retval != 0) { break; } } } /* * We've reached EOF, or our partner has reached EOF. */ if ((dev->flags & CAMDD_DEV_FLAG_EOF) || (dev->flags & CAMDD_DEV_FLAG_PEER_EOF)) { if (dev->write_dev != 0) { if ((STAILQ_EMPTY(&dev->work_queue)) && (dev->num_run_queue == 0) && (dev->cur_active_io == 0)) { goto bailout; } } else { /* * If we're the reader, and the writer * got EOF, he is already done. If we got * the EOF, then we need to wait until * everything is flushed out for the writer. */ if (dev->flags & CAMDD_DEV_FLAG_PEER_EOF) { goto bailout; } else if ((dev->num_peer_work_queue == 0) && (dev->num_peer_done_queue == 0) && (dev->cur_active_io == 0) && (dev->num_run_queue == 0)) { goto bailout; } } /* * XXX KDM need to do something about the pending * queue and cleanup resources. */ } if ((dev->write_dev == 0) && (dev->cur_active_io == 0) && (dev->peer_bytes_queued < dev->peer_dev->blocksize)) kq_ts = &ts; else kq_ts = NULL; /* * Run kevent to see if there are events to process. */ pthread_mutex_unlock(&dev->mutex); retval = kevent(dev->kq, NULL, 0, &ke, 1, kq_ts); pthread_mutex_lock(&dev->mutex); if (retval == -1) { warn("%s: error returned from kevent",__func__); goto bailout; } else if (retval != 0) { switch (ke.filter) { case EVFILT_READ: if (dev->fetch != NULL) { retval = dev->fetch(dev); if (retval == -1) { error_exit = 1; goto bailout; } } break; case EVFILT_SIGNAL: /* * We register for this so we don't get * an error as a result of a SIGINFO or a * SIGINT. It will actually get handled * by the signal handler. If we get a * SIGINT, bail out without printing an * error message. Any other signals * will result in the error message above. */ if (ke.ident == SIGINT) goto bailout; break; case EVFILT_USER: retval = 0; /* * Check to see if the other thread has * queued any I/O for us to do. (In this * case we're the writer.) */ for (buf = STAILQ_FIRST(&dev->work_queue); buf != NULL; buf = STAILQ_FIRST(&dev->work_queue)) { STAILQ_REMOVE_HEAD(&dev->work_queue, work_links); retval = camdd_queue(dev, buf); /* * We keep going unless we get an * actual error. If we get EOF, we * still want to remove the buffers * from the queue and send the back * to the reader thread. */ if (retval == -1) { error_exit = 1; goto bailout; } else retval = 0; } /* * Next check to see if the other thread has * queued any completed buffers back to us. * (In this case we're the reader.) */ for (buf = STAILQ_FIRST(&dev->peer_done_queue); buf != NULL; buf = STAILQ_FIRST(&dev->peer_done_queue)){ STAILQ_REMOVE_HEAD( &dev->peer_done_queue, work_links); dev->num_peer_done_queue--; camdd_peer_done(buf); } break; default: warnx("%s: unknown kevent filter %d", __func__, ke.filter); break; } } } bailout: dev->flags &= ~CAMDD_DEV_FLAG_ACTIVE; /* XXX KDM cleanup resources here? */ pthread_mutex_unlock(&dev->mutex); need_exit = 1; sem_post(&camdd_sem); return (NULL); } /* * Simplistic translation of CCB status to our local status. */ camdd_buf_status camdd_ccb_status(union ccb *ccb) { camdd_buf_status status = CAMDD_STATUS_NONE; cam_status ccb_status; ccb_status = ccb->ccb_h.status & CAM_STATUS_MASK; switch (ccb_status) { case CAM_REQ_CMP: { if (ccb->csio.resid == 0) { status = CAMDD_STATUS_OK; } else if (ccb->csio.dxfer_len > ccb->csio.resid) { status = CAMDD_STATUS_SHORT_IO; } else { status = CAMDD_STATUS_EOF; } break; } case CAM_SCSI_STATUS_ERROR: { switch (ccb->csio.scsi_status) { case SCSI_STATUS_OK: case SCSI_STATUS_COND_MET: case SCSI_STATUS_INTERMED: case SCSI_STATUS_INTERMED_COND_MET: status = CAMDD_STATUS_OK; break; case SCSI_STATUS_CMD_TERMINATED: case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_QUEUE_FULL: case SCSI_STATUS_BUSY: case SCSI_STATUS_RESERV_CONFLICT: default: status = CAMDD_STATUS_ERROR; break; } break; } default: status = CAMDD_STATUS_ERROR; break; } return (status); } /* * Queue a buffer to our peer's work thread for writing. * * Returns 0 for success, -1 for failure, 1 if the other thread exited. */ int camdd_queue_peer_buf(struct camdd_dev *dev, struct camdd_buf *buf) { struct kevent ke; STAILQ_HEAD(, camdd_buf) local_queue; struct camdd_buf *buf1, *buf2; struct camdd_buf_data *data = NULL; uint64_t peer_bytes_queued = 0; int active = 1; int retval = 0; STAILQ_INIT(&local_queue); /* * Since we're the reader, we need to queue our I/O to the writer * in sequential order in order to make sure it gets written out * in sequential order. * * Check the next expected I/O starting offset. If this doesn't * match, put it on the reorder queue. */ if ((buf->lba * dev->sector_size) != dev->next_completion_pos_bytes) { /* * If there is nothing on the queue, there is no sorting * needed. */ if (STAILQ_EMPTY(&dev->reorder_queue)) { STAILQ_INSERT_TAIL(&dev->reorder_queue, buf, links); dev->num_reorder_queue++; goto bailout; } /* * Sort in ascending order by starting LBA. There should * be no identical LBAs. */ for (buf1 = STAILQ_FIRST(&dev->reorder_queue); buf1 != NULL; buf1 = buf2) { buf2 = STAILQ_NEXT(buf1, links); if (buf->lba < buf1->lba) { /* * If we're less than the first one, then * we insert at the head of the list * because this has to be the first element * on the list. */ STAILQ_INSERT_HEAD(&dev->reorder_queue, buf, links); dev->num_reorder_queue++; break; } else if (buf->lba > buf1->lba) { if (buf2 == NULL) { STAILQ_INSERT_TAIL(&dev->reorder_queue, buf, links); dev->num_reorder_queue++; break; } else if (buf->lba < buf2->lba) { STAILQ_INSERT_AFTER(&dev->reorder_queue, buf1, buf, links); dev->num_reorder_queue++; break; } } else { errx(1, "Found buffers with duplicate LBA %ju!", buf->lba); } } goto bailout; } else { /* * We're the next expected I/O completion, so put ourselves * on the local queue to be sent to the writer. We use * work_links here so that we can queue this to the * peer_work_queue before taking the buffer off of the * local_queue. */ dev->next_completion_pos_bytes += buf->len; STAILQ_INSERT_TAIL(&local_queue, buf, work_links); /* * Go through the reorder queue looking for more sequential * I/O and add it to the local queue. */ for (buf1 = STAILQ_FIRST(&dev->reorder_queue); buf1 != NULL; buf1 = STAILQ_FIRST(&dev->reorder_queue)) { /* * As soon as we see an I/O that is out of sequence, * we're done. */ if ((buf1->lba * dev->sector_size) != dev->next_completion_pos_bytes) break; STAILQ_REMOVE_HEAD(&dev->reorder_queue, links); dev->num_reorder_queue--; STAILQ_INSERT_TAIL(&local_queue, buf1, work_links); dev->next_completion_pos_bytes += buf1->len; } } /* * Setup the event to let the other thread know that it has work * pending. */ EV_SET(&ke, (uintptr_t)&dev->peer_dev->work_queue, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); /* * Put this on our shadow queue so that we know what we've queued * to the other thread. */ STAILQ_FOREACH_SAFE(buf1, &local_queue, work_links, buf2) { if (buf1->buf_type != CAMDD_BUF_DATA) { errx(1, "%s: should have a data buffer, not an " "indirect buffer", __func__); } data = &buf1->buf_type_spec.data; /* * We only need to send one EOF to the writer, and don't * need to continue sending EOFs after that. */ if (buf1->status == CAMDD_STATUS_EOF) { if (dev->flags & CAMDD_DEV_FLAG_EOF_SENT) { STAILQ_REMOVE(&local_queue, buf1, camdd_buf, work_links); camdd_release_buf(buf1); retval = 1; continue; } dev->flags |= CAMDD_DEV_FLAG_EOF_SENT; } STAILQ_INSERT_TAIL(&dev->peer_work_queue, buf1, links); peer_bytes_queued += (data->fill_len - data->resid); dev->peer_bytes_queued += (data->fill_len - data->resid); dev->num_peer_work_queue++; } if (STAILQ_FIRST(&local_queue) == NULL) goto bailout; /* * Drop our mutex and pick up the other thread's mutex. We need to * do this to avoid deadlocks. */ pthread_mutex_unlock(&dev->mutex); pthread_mutex_lock(&dev->peer_dev->mutex); if (dev->peer_dev->flags & CAMDD_DEV_FLAG_ACTIVE) { /* * Put the buffers on the other thread's incoming work queue. */ for (buf1 = STAILQ_FIRST(&local_queue); buf1 != NULL; buf1 = STAILQ_FIRST(&local_queue)) { STAILQ_REMOVE_HEAD(&local_queue, work_links); STAILQ_INSERT_TAIL(&dev->peer_dev->work_queue, buf1, work_links); } /* * Send an event to the other thread's kqueue to let it know * that there is something on the work queue. */ retval = kevent(dev->peer_dev->kq, &ke, 1, NULL, 0, NULL); if (retval == -1) warn("%s: unable to add peer work_queue kevent", __func__); else retval = 0; } else active = 0; pthread_mutex_unlock(&dev->peer_dev->mutex); pthread_mutex_lock(&dev->mutex); /* * If the other side isn't active, run through the queue and * release all of the buffers. */ if (active == 0) { for (buf1 = STAILQ_FIRST(&local_queue); buf1 != NULL; buf1 = STAILQ_FIRST(&local_queue)) { STAILQ_REMOVE_HEAD(&local_queue, work_links); STAILQ_REMOVE(&dev->peer_work_queue, buf1, camdd_buf, links); dev->num_peer_work_queue--; camdd_release_buf(buf1); } dev->peer_bytes_queued -= peer_bytes_queued; retval = 1; } bailout: return (retval); } /* * Return a buffer to the reader thread when we have completed writing it. */ int camdd_complete_peer_buf(struct camdd_dev *dev, struct camdd_buf *peer_buf) { struct kevent ke; int retval = 0; /* * Setup the event to let the other thread know that we have * completed a buffer. */ EV_SET(&ke, (uintptr_t)&dev->peer_dev->peer_done_queue, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); /* * Drop our lock and acquire the other thread's lock before * manipulating */ pthread_mutex_unlock(&dev->mutex); pthread_mutex_lock(&dev->peer_dev->mutex); /* * Put the buffer on the reader thread's peer done queue now that * we have completed it. */ STAILQ_INSERT_TAIL(&dev->peer_dev->peer_done_queue, peer_buf, work_links); dev->peer_dev->num_peer_done_queue++; /* * Send an event to the peer thread to let it know that we've added * something to its peer done queue. */ retval = kevent(dev->peer_dev->kq, &ke, 1, NULL, 0, NULL); if (retval == -1) warn("%s: unable to add peer_done_queue kevent", __func__); else retval = 0; /* * Drop the other thread's lock and reacquire ours. */ pthread_mutex_unlock(&dev->peer_dev->mutex); pthread_mutex_lock(&dev->mutex); return (retval); } /* * Free a buffer that was written out by the writer thread and returned to * the reader thread. */ void camdd_peer_done(struct camdd_buf *buf) { struct camdd_dev *dev; struct camdd_buf_data *data; dev = buf->dev; if (buf->buf_type != CAMDD_BUF_DATA) { errx(1, "%s: should have a data buffer, not an " "indirect buffer", __func__); } data = &buf->buf_type_spec.data; STAILQ_REMOVE(&dev->peer_work_queue, buf, camdd_buf, links); dev->num_peer_work_queue--; dev->peer_bytes_queued -= (data->fill_len - data->resid); if (buf->status == CAMDD_STATUS_EOF) dev->flags |= CAMDD_DEV_FLAG_PEER_EOF; STAILQ_INSERT_TAIL(&dev->free_queue, buf, links); } /* * Assumes caller holds the lock for this device. */ void camdd_complete_buf(struct camdd_dev *dev, struct camdd_buf *buf, int *error_count) { int retval = 0; /* * If we're the reader, we need to send the completed I/O * to the writer. If we're the writer, we need to just * free up resources, or let the reader know if we've * encountered an error. */ if (dev->write_dev == 0) { retval = camdd_queue_peer_buf(dev, buf); if (retval != 0) (*error_count)++; } else { struct camdd_buf *tmp_buf, *next_buf; STAILQ_FOREACH_SAFE(tmp_buf, &buf->src_list, src_links, next_buf) { struct camdd_buf *src_buf; struct camdd_buf_indirect *indirect; STAILQ_REMOVE(&buf->src_list, tmp_buf, camdd_buf, src_links); tmp_buf->status = buf->status; if (tmp_buf->buf_type == CAMDD_BUF_DATA) { camdd_complete_peer_buf(dev, tmp_buf); continue; } indirect = &tmp_buf->buf_type_spec.indirect; src_buf = indirect->src_buf; src_buf->refcount--; /* * XXX KDM we probably need to account for * exactly how many bytes we were able to * write. Allocate the residual to the * first N buffers? Or just track the * number of bytes written? Right now the reader * doesn't do anything with a residual. */ src_buf->status = buf->status; if (src_buf->refcount <= 0) camdd_complete_peer_buf(dev, src_buf); STAILQ_INSERT_TAIL(&dev->free_indirect_queue, tmp_buf, links); } STAILQ_INSERT_TAIL(&dev->free_queue, buf, links); } } /* * Fetch all completed commands from the pass(4) device. * * Returns the number of commands received, or -1 if any of the commands * completed with an error. Returns 0 if no commands are available. */ int camdd_pass_fetch(struct camdd_dev *dev) { struct camdd_dev_pass *pass_dev = &dev->dev_spec.pass; union ccb ccb; int retval = 0, num_fetched = 0, error_count = 0; pthread_mutex_unlock(&dev->mutex); /* * XXX KDM we don't distinguish between EFAULT and ENOENT. */ while ((retval = ioctl(pass_dev->dev->fd, CAMIOGET, &ccb)) != -1) { struct camdd_buf *buf; struct camdd_buf_data *data; cam_status ccb_status; union ccb *buf_ccb; buf = ccb.ccb_h.ccb_buf; data = &buf->buf_type_spec.data; buf_ccb = &data->ccb; num_fetched++; /* * Copy the CCB back out so we get status, sense data, etc. */ bcopy(&ccb, buf_ccb, sizeof(ccb)); pthread_mutex_lock(&dev->mutex); /* * We're now done, so take this off the active queue. */ STAILQ_REMOVE(&dev->active_queue, buf, camdd_buf, links); dev->cur_active_io--; ccb_status = ccb.ccb_h.status & CAM_STATUS_MASK; if (ccb_status != CAM_REQ_CMP) { cam_error_print(pass_dev->dev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); } data->resid = ccb.csio.resid; dev->bytes_transferred += (ccb.csio.dxfer_len - ccb.csio.resid); if (buf->status == CAMDD_STATUS_NONE) buf->status = camdd_ccb_status(&ccb); if (buf->status == CAMDD_STATUS_ERROR) error_count++; else if (buf->status == CAMDD_STATUS_EOF) { /* * Once we queue this buffer to our partner thread, * he will know that we've hit EOF. */ dev->flags |= CAMDD_DEV_FLAG_EOF; } camdd_complete_buf(dev, buf, &error_count); /* * Unlock in preparation for the ioctl call. */ pthread_mutex_unlock(&dev->mutex); } pthread_mutex_lock(&dev->mutex); if (error_count > 0) return (-1); else return (num_fetched); } /* * Returns -1 for error, 0 for success/continue, and 1 for resource * shortage/stop processing. */ int camdd_file_run(struct camdd_dev *dev) { struct camdd_dev_file *file_dev = &dev->dev_spec.file; struct camdd_buf_data *data; struct camdd_buf *buf; off_t io_offset; int retval = 0, write_dev = dev->write_dev; int error_count = 0, no_resources = 0, double_buf_needed = 0; uint32_t num_sectors = 0, db_len = 0; buf = STAILQ_FIRST(&dev->run_queue); if (buf == NULL) { no_resources = 1; goto bailout; } else if ((dev->write_dev == 0) && (dev->flags & (CAMDD_DEV_FLAG_EOF | CAMDD_DEV_FLAG_EOF_SENT))) { STAILQ_REMOVE(&dev->run_queue, buf, camdd_buf, links); dev->num_run_queue--; buf->status = CAMDD_STATUS_EOF; error_count++; goto bailout; } /* * If we're writing, we need to go through the source buffer list * and create an S/G list. */ if (write_dev != 0) { retval = camdd_buf_sg_create(buf, /*iovec*/ 1, dev->sector_size, &num_sectors, &double_buf_needed); if (retval != 0) { no_resources = 1; goto bailout; } } STAILQ_REMOVE(&dev->run_queue, buf, camdd_buf, links); dev->num_run_queue--; data = &buf->buf_type_spec.data; /* * pread(2) and pwrite(2) offsets are byte offsets. */ io_offset = buf->lba * dev->sector_size; /* * Unlock the mutex while we read or write. */ pthread_mutex_unlock(&dev->mutex); /* * Note that we don't need to double buffer if we're the reader * because in that case, we have allocated a single buffer of * sufficient size to do the read. This copy is necessary on * writes because if one of the components of the S/G list is not * a sector size multiple, the kernel will reject the write. This * is unfortunate but not surprising. So this will make sure that * we're using a single buffer that is a multiple of the sector size. */ if ((double_buf_needed != 0) && (data->sg_count > 1) && (write_dev != 0)) { uint32_t cur_offset; int i; if (file_dev->tmp_buf == NULL) file_dev->tmp_buf = calloc(dev->blocksize, 1); if (file_dev->tmp_buf == NULL) { buf->status = CAMDD_STATUS_ERROR; error_count++; goto bailout; } for (i = 0, cur_offset = 0; i < data->sg_count; i++) { bcopy(data->iovec[i].iov_base, &file_dev->tmp_buf[cur_offset], data->iovec[i].iov_len); cur_offset += data->iovec[i].iov_len; } db_len = cur_offset; } if (file_dev->file_flags & CAMDD_FF_CAN_SEEK) { if (write_dev == 0) { /* * XXX KDM is there any way we would need a S/G * list here? */ retval = pread(file_dev->fd, data->buf, buf->len, io_offset); } else { if (double_buf_needed != 0) { retval = pwrite(file_dev->fd, file_dev->tmp_buf, db_len, io_offset); } else if (data->sg_count == 0) { retval = pwrite(file_dev->fd, data->buf, data->fill_len, io_offset); } else { retval = pwritev(file_dev->fd, data->iovec, data->sg_count, io_offset); } } } else { if (write_dev == 0) { /* * XXX KDM is there any way we would need a S/G * list here? */ retval = read(file_dev->fd, data->buf, buf->len); } else { if (double_buf_needed != 0) { retval = write(file_dev->fd, file_dev->tmp_buf, db_len); } else if (data->sg_count == 0) { retval = write(file_dev->fd, data->buf, data->fill_len); } else { retval = writev(file_dev->fd, data->iovec, data->sg_count); } } } /* We're done, re-acquire the lock */ pthread_mutex_lock(&dev->mutex); if (retval >= (ssize_t)data->fill_len) { /* * If the bytes transferred is more than the request size, * that indicates an overrun, which should only happen at * the end of a transfer if we have to round up to a sector * boundary. */ if (buf->status == CAMDD_STATUS_NONE) buf->status = CAMDD_STATUS_OK; data->resid = 0; dev->bytes_transferred += retval; } else if (retval == -1) { warn("Error %s %s", (write_dev) ? "writing to" : "reading from", file_dev->filename); buf->status = CAMDD_STATUS_ERROR; data->resid = data->fill_len; error_count++; if (dev->debug == 0) goto bailout; if ((double_buf_needed != 0) && (write_dev != 0)) { fprintf(stderr, "%s: fd %d, DB buf %p, len %u lba %ju " "offset %ju\n", __func__, file_dev->fd, file_dev->tmp_buf, db_len, (uintmax_t)buf->lba, (uintmax_t)io_offset); } else if (data->sg_count == 0) { fprintf(stderr, "%s: fd %d, buf %p, len %u, lba %ju " "offset %ju\n", __func__, file_dev->fd, data->buf, data->fill_len, (uintmax_t)buf->lba, (uintmax_t)io_offset); } else { int i; fprintf(stderr, "%s: fd %d, len %u, lba %ju " "offset %ju\n", __func__, file_dev->fd, data->fill_len, (uintmax_t)buf->lba, (uintmax_t)io_offset); for (i = 0; i < data->sg_count; i++) { fprintf(stderr, "index %d ptr %p len %zu\n", i, data->iovec[i].iov_base, data->iovec[i].iov_len); } } } else if (retval == 0) { buf->status = CAMDD_STATUS_EOF; if (dev->debug != 0) printf("%s: got EOF from %s!\n", __func__, file_dev->filename); data->resid = data->fill_len; error_count++; } else if (retval < (ssize_t)data->fill_len) { if (buf->status == CAMDD_STATUS_NONE) buf->status = CAMDD_STATUS_SHORT_IO; data->resid = data->fill_len - retval; dev->bytes_transferred += retval; } bailout: if (buf != NULL) { if (buf->status == CAMDD_STATUS_EOF) { struct camdd_buf *buf2; dev->flags |= CAMDD_DEV_FLAG_EOF; STAILQ_FOREACH(buf2, &dev->run_queue, links) buf2->status = CAMDD_STATUS_EOF; } camdd_complete_buf(dev, buf, &error_count); } if (error_count != 0) return (-1); else if (no_resources != 0) return (1); else return (0); } /* * Execute one command from the run queue. Returns 0 for success, 1 for * stop processing, and -1 for error. */ int camdd_pass_run(struct camdd_dev *dev) { struct camdd_buf *buf = NULL; struct camdd_dev_pass *pass_dev = &dev->dev_spec.pass; struct camdd_buf_data *data; uint32_t num_blocks, sectors_used = 0; union ccb *ccb; int retval = 0, is_write = dev->write_dev; int double_buf_needed = 0; buf = STAILQ_FIRST(&dev->run_queue); if (buf == NULL) { retval = 1; goto bailout; } /* * If we're writing, we need to go through the source buffer list * and create an S/G list. */ if (is_write != 0) { retval = camdd_buf_sg_create(buf, /*iovec*/ 0,dev->sector_size, §ors_used, &double_buf_needed); if (retval != 0) { retval = -1; goto bailout; } } STAILQ_REMOVE(&dev->run_queue, buf, camdd_buf, links); dev->num_run_queue--; data = &buf->buf_type_spec.data; ccb = &data->ccb; bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); /* * In almost every case the number of blocks should be the device * block size. The exception may be at the end of an I/O stream * for a partial block or at the end of a device. */ if (is_write != 0) num_blocks = sectors_used; else num_blocks = data->fill_len / pass_dev->block_len; scsi_read_write(&ccb->csio, /*retries*/ dev->retry_count, /*cbfcnp*/ NULL, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*readop*/ (dev->write_dev == 0) ? SCSI_RW_READ : SCSI_RW_WRITE, /*byte2*/ 0, /*minimum_cmd_size*/ dev->min_cmd_size, /*lba*/ buf->lba, /*block_count*/ num_blocks, /*data_ptr*/ (data->sg_count != 0) ? (uint8_t *)data->segs : data->buf, /*dxfer_len*/ (num_blocks * pass_dev->block_len), /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ dev->io_timeout); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (dev->retry_count != 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (data->sg_count != 0) { ccb->csio.sglist_cnt = data->sg_count; ccb->ccb_h.flags |= CAM_DATA_SG; } /* * Store a pointer to the buffer in the CCB. The kernel will * restore this when we get it back, and we'll use it to identify * the buffer this CCB came from. */ ccb->ccb_h.ccb_buf = buf; /* * Unlock our mutex in preparation for issuing the ioctl. */ pthread_mutex_unlock(&dev->mutex); /* * Queue the CCB to the pass(4) driver. */ if (ioctl(pass_dev->dev->fd, CAMIOQUEUE, ccb) == -1) { pthread_mutex_lock(&dev->mutex); warn("%s: error sending CAMIOQUEUE ioctl to %s%u", __func__, pass_dev->dev->device_name, pass_dev->dev->dev_unit_num); warn("%s: CCB address is %p", __func__, ccb); retval = -1; STAILQ_INSERT_TAIL(&dev->free_queue, buf, links); } else { pthread_mutex_lock(&dev->mutex); dev->cur_active_io++; STAILQ_INSERT_TAIL(&dev->active_queue, buf, links); } bailout: return (retval); } int camdd_get_next_lba_len(struct camdd_dev *dev, uint64_t *lba, ssize_t *len) { struct camdd_dev_pass *pass_dev; uint32_t num_blocks; int retval = 0; pass_dev = &dev->dev_spec.pass; *lba = dev->next_io_pos_bytes / dev->sector_size; *len = dev->blocksize; num_blocks = *len / dev->sector_size; /* * If max_sector is 0, then we have no set limit. This can happen * if we're writing to a file in a filesystem, or reading from * something like /dev/zero. */ if ((dev->max_sector != 0) || (dev->sector_io_limit != 0)) { uint64_t max_sector; if ((dev->max_sector != 0) && (dev->sector_io_limit != 0)) max_sector = min(dev->sector_io_limit, dev->max_sector); else if (dev->max_sector != 0) max_sector = dev->max_sector; else max_sector = dev->sector_io_limit; /* * Check to see whether we're starting off past the end of * the device. If so, we need to just send an EOF * notification to the writer. */ if (*lba > max_sector) { *len = 0; retval = 1; } else if (((*lba + num_blocks) > max_sector + 1) || ((*lba + num_blocks) < *lba)) { /* * If we get here (but pass the first check), we * can trim the request length down to go to the * end of the device. */ num_blocks = (max_sector + 1) - *lba; *len = num_blocks * dev->sector_size; retval = 1; } } dev->next_io_pos_bytes += *len; return (retval); } /* * Returns 0 for success, 1 for EOF detected, and -1 for failure. */ int camdd_queue(struct camdd_dev *dev, struct camdd_buf *read_buf) { struct camdd_buf *buf = NULL; struct camdd_buf_data *data; struct camdd_dev_pass *pass_dev; size_t new_len; struct camdd_buf_data *rb_data; int is_write = dev->write_dev; int eof_flush_needed = 0; int retval = 0; int error; pass_dev = &dev->dev_spec.pass; /* * If we've gotten EOF or our partner has, we should not continue * queueing I/O. If we're a writer, though, we should continue * to write any buffers that don't have EOF status. */ if ((dev->flags & CAMDD_DEV_FLAG_EOF) || ((dev->flags & CAMDD_DEV_FLAG_PEER_EOF) && (is_write == 0))) { /* * Tell the worker thread that we have seen EOF. */ retval = 1; /* * If we're the writer, send the buffer back with EOF status. */ if (is_write) { read_buf->status = CAMDD_STATUS_EOF; error = camdd_complete_peer_buf(dev, read_buf); } goto bailout; } if (is_write == 0) { buf = camdd_get_buf(dev, CAMDD_BUF_DATA); if (buf == NULL) { retval = -1; goto bailout; } data = &buf->buf_type_spec.data; retval = camdd_get_next_lba_len(dev, &buf->lba, &buf->len); if (retval != 0) { buf->status = CAMDD_STATUS_EOF; if ((buf->len == 0) && ((dev->flags & (CAMDD_DEV_FLAG_EOF_SENT | CAMDD_DEV_FLAG_EOF_QUEUED)) != 0)) { camdd_release_buf(buf); goto bailout; } dev->flags |= CAMDD_DEV_FLAG_EOF_QUEUED; } data->fill_len = buf->len; data->src_start_offset = buf->lba * dev->sector_size; /* * Put this on the run queue. */ STAILQ_INSERT_TAIL(&dev->run_queue, buf, links); dev->num_run_queue++; /* We're done. */ goto bailout; } /* * Check for new EOF status from the reader. */ if ((read_buf->status == CAMDD_STATUS_EOF) || (read_buf->status == CAMDD_STATUS_ERROR)) { dev->flags |= CAMDD_DEV_FLAG_PEER_EOF; if ((STAILQ_FIRST(&dev->pending_queue) == NULL) && (read_buf->len == 0)) { camdd_complete_peer_buf(dev, read_buf); retval = 1; goto bailout; } else eof_flush_needed = 1; } /* * See if we have a buffer we're composing with pieces from our * partner thread. */ buf = STAILQ_FIRST(&dev->pending_queue); if (buf == NULL) { uint64_t lba; ssize_t len; retval = camdd_get_next_lba_len(dev, &lba, &len); if (retval != 0) { read_buf->status = CAMDD_STATUS_EOF; if (len == 0) { dev->flags |= CAMDD_DEV_FLAG_EOF; error = camdd_complete_peer_buf(dev, read_buf); goto bailout; } } /* * If we don't have a pending buffer, we need to grab a new * one from the free list or allocate another one. */ buf = camdd_get_buf(dev, CAMDD_BUF_DATA); if (buf == NULL) { retval = 1; goto bailout; } buf->lba = lba; buf->len = len; STAILQ_INSERT_TAIL(&dev->pending_queue, buf, links); dev->num_pending_queue++; } data = &buf->buf_type_spec.data; rb_data = &read_buf->buf_type_spec.data; if ((rb_data->src_start_offset != dev->next_peer_pos_bytes) && (dev->debug != 0)) { printf("%s: WARNING: reader offset %#jx != expected offset " "%#jx\n", __func__, (uintmax_t)rb_data->src_start_offset, (uintmax_t)dev->next_peer_pos_bytes); } dev->next_peer_pos_bytes = rb_data->src_start_offset + (rb_data->fill_len - rb_data->resid); new_len = (rb_data->fill_len - rb_data->resid) + data->fill_len; if (new_len < buf->len) { /* * There are three cases here: * 1. We need more data to fill up a block, so we put * this I/O on the queue and wait for more I/O. * 2. We have a pending buffer in the queue that is * smaller than our blocksize, but we got an EOF. So we * need to go ahead and flush the write out. * 3. We got an error. */ /* * Increment our fill length. */ data->fill_len += (rb_data->fill_len - rb_data->resid); /* * Add the new read buffer to the list for writing. */ STAILQ_INSERT_TAIL(&buf->src_list, read_buf, src_links); /* Increment the count */ buf->src_count++; if (eof_flush_needed == 0) { /* * We need to exit, because we don't have enough * data yet. */ goto bailout; } else { /* * Take the buffer off of the pending queue. */ STAILQ_REMOVE(&dev->pending_queue, buf, camdd_buf, links); dev->num_pending_queue--; /* * If we need an EOF flush, but there is no data * to flush, go ahead and return this buffer. */ if (data->fill_len == 0) { camdd_complete_buf(dev, buf, /*error_count*/0); retval = 1; goto bailout; } /* * Put this on the next queue for execution. */ STAILQ_INSERT_TAIL(&dev->run_queue, buf, links); dev->num_run_queue++; } } else if (new_len == buf->len) { /* * We have enough data to completey fill one block, * so we're ready to issue the I/O. */ /* * Take the buffer off of the pending queue. */ STAILQ_REMOVE(&dev->pending_queue, buf, camdd_buf, links); dev->num_pending_queue--; /* * Add the new read buffer to the list for writing. */ STAILQ_INSERT_TAIL(&buf->src_list, read_buf, src_links); /* Increment the count */ buf->src_count++; /* * Increment our fill length. */ data->fill_len += (rb_data->fill_len - rb_data->resid); /* * Put this on the next queue for execution. */ STAILQ_INSERT_TAIL(&dev->run_queue, buf, links); dev->num_run_queue++; } else { struct camdd_buf *idb; struct camdd_buf_indirect *indirect; uint32_t len_to_go, cur_offset; idb = camdd_get_buf(dev, CAMDD_BUF_INDIRECT); if (idb == NULL) { retval = 1; goto bailout; } indirect = &idb->buf_type_spec.indirect; indirect->src_buf = read_buf; read_buf->refcount++; indirect->offset = 0; indirect->start_ptr = rb_data->buf; /* * We've already established that there is more * data in read_buf than we have room for in our * current write request. So this particular chunk * of the request should just be the remainder * needed to fill up a block. */ indirect->len = buf->len - (data->fill_len - data->resid); camdd_buf_add_child(buf, idb); /* * This buffer is ready to execute, so we can take * it off the pending queue and put it on the run * queue. */ STAILQ_REMOVE(&dev->pending_queue, buf, camdd_buf, links); dev->num_pending_queue--; STAILQ_INSERT_TAIL(&dev->run_queue, buf, links); dev->num_run_queue++; cur_offset = indirect->offset + indirect->len; /* * The resulting I/O would be too large to fit in * one block. We need to split this I/O into * multiple pieces. Allocate as many buffers as needed. */ for (len_to_go = rb_data->fill_len - rb_data->resid - indirect->len; len_to_go > 0;) { struct camdd_buf *new_buf; struct camdd_buf_data *new_data; uint64_t lba; ssize_t len; retval = camdd_get_next_lba_len(dev, &lba, &len); if ((retval != 0) && (len == 0)) { /* * The device has already been marked * as EOF, and there is no space left. */ goto bailout; } new_buf = camdd_get_buf(dev, CAMDD_BUF_DATA); if (new_buf == NULL) { retval = 1; goto bailout; } new_buf->lba = lba; new_buf->len = len; idb = camdd_get_buf(dev, CAMDD_BUF_INDIRECT); if (idb == NULL) { retval = 1; goto bailout; } indirect = &idb->buf_type_spec.indirect; indirect->src_buf = read_buf; read_buf->refcount++; indirect->offset = cur_offset; indirect->start_ptr = rb_data->buf + cur_offset; indirect->len = min(len_to_go, new_buf->len); #if 0 if (((indirect->len % dev->sector_size) != 0) || ((indirect->offset % dev->sector_size) != 0)) { warnx("offset %ju len %ju not aligned with " "sector size %u", indirect->offset, (uintmax_t)indirect->len, dev->sector_size); } #endif cur_offset += indirect->len; len_to_go -= indirect->len; camdd_buf_add_child(new_buf, idb); new_data = &new_buf->buf_type_spec.data; if ((new_data->fill_len == new_buf->len) || (eof_flush_needed != 0)) { STAILQ_INSERT_TAIL(&dev->run_queue, new_buf, links); dev->num_run_queue++; } else if (new_data->fill_len < buf->len) { STAILQ_INSERT_TAIL(&dev->pending_queue, new_buf, links); dev->num_pending_queue++; } else { warnx("%s: too much data in new " "buffer!", __func__); retval = 1; goto bailout; } } } bailout: return (retval); } void camdd_get_depth(struct camdd_dev *dev, uint32_t *our_depth, uint32_t *peer_depth, uint32_t *our_bytes, uint32_t *peer_bytes) { *our_depth = dev->cur_active_io + dev->num_run_queue; if (dev->num_peer_work_queue > dev->num_peer_done_queue) *peer_depth = dev->num_peer_work_queue - dev->num_peer_done_queue; else *peer_depth = 0; *our_bytes = *our_depth * dev->blocksize; *peer_bytes = dev->peer_bytes_queued; } void camdd_sig_handler(int sig) { if (sig == SIGINFO) need_status = 1; else { need_exit = 1; error_exit = 1; } sem_post(&camdd_sem); } void camdd_print_status(struct camdd_dev *camdd_dev, struct camdd_dev *other_dev, struct timespec *start_time) { struct timespec done_time; uint64_t total_ns; long double mb_sec, total_sec; int error = 0; error = clock_gettime(CLOCK_MONOTONIC_PRECISE, &done_time); if (error != 0) { warn("Unable to get done time"); return; } timespecsub(&done_time, start_time); total_ns = done_time.tv_nsec + (done_time.tv_sec * 1000000000); total_sec = total_ns; total_sec /= 1000000000; fprintf(stderr, "%ju bytes %s %s\n%ju bytes %s %s\n" "%.4Lf seconds elapsed\n", (uintmax_t)camdd_dev->bytes_transferred, (camdd_dev->write_dev == 0) ? "read from" : "written to", camdd_dev->device_name, (uintmax_t)other_dev->bytes_transferred, (other_dev->write_dev == 0) ? "read from" : "written to", other_dev->device_name, total_sec); mb_sec = min(other_dev->bytes_transferred,camdd_dev->bytes_transferred); mb_sec /= 1024 * 1024; mb_sec *= 1000000000; mb_sec /= total_ns; fprintf(stderr, "%.2Lf MB/sec\n", mb_sec); } int camdd_rw(struct camdd_io_opts *io_opts, int num_io_opts, uint64_t max_io, int retry_count, int timeout) { char *device = NULL; struct cam_device *new_cam_dev = NULL; struct camdd_dev *devs[2]; struct timespec start_time; pthread_t threads[2]; int unit = 0; int error = 0; int i; if (num_io_opts != 2) { warnx("Must have one input and one output path"); error = 1; goto bailout; } bzero(devs, sizeof(devs)); for (i = 0; i < num_io_opts; i++) { switch (io_opts[i].dev_type) { case CAMDD_DEV_PASS: { camdd_argmask new_arglist = CAMDD_ARG_NONE; int bus = 0, target = 0, lun = 0; char name[30]; int rv; if (isdigit(io_opts[i].dev_name[0])) { /* device specified as bus:target[:lun] */ rv = parse_btl(io_opts[i].dev_name, &bus, &target, &lun, &new_arglist); if (rv < 2) { warnx("numeric device specification " "must be either bus:target, or " "bus:target:lun"); error = 1; goto bailout; } /* default to 0 if lun was not specified */ if ((new_arglist & CAMDD_ARG_LUN) == 0) { lun = 0; new_arglist |= CAMDD_ARG_LUN; } } else { if (cam_get_device(io_opts[i].dev_name, name, sizeof name, &unit) == -1) { warnx("%s", cam_errbuf); error = 1; goto bailout; } device = strdup(name); new_arglist |= CAMDD_ARG_DEVICE |CAMDD_ARG_UNIT; } if (new_arglist & (CAMDD_ARG_BUS | CAMDD_ARG_TARGET)) new_cam_dev = cam_open_btl(bus, target, lun, O_RDWR, NULL); else new_cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL); if (new_cam_dev == NULL) { warnx("%s", cam_errbuf); error = 1; goto bailout; } devs[i] = camdd_probe_pass(new_cam_dev, /*io_opts*/ &io_opts[i], CAMDD_ARG_ERR_RECOVER, /*probe_retry_count*/ 3, /*probe_timeout*/ 5000, /*io_retry_count*/ retry_count, /*io_timeout*/ timeout); if (devs[i] == NULL) { warn("Unable to probe device %s%u", new_cam_dev->device_name, new_cam_dev->dev_unit_num); error = 1; goto bailout; } break; } case CAMDD_DEV_FILE: { int fd = -1; if (io_opts[i].dev_name[0] == '-') { if (io_opts[i].write_dev != 0) fd = STDOUT_FILENO; else fd = STDIN_FILENO; } else { if (io_opts[i].write_dev != 0) { fd = open(io_opts[i].dev_name, O_RDWR | O_CREAT, S_IWUSR |S_IRUSR); } else { fd = open(io_opts[i].dev_name, O_RDONLY); } } if (fd == -1) { warn("error opening file %s", io_opts[i].dev_name); error = 1; goto bailout; } devs[i] = camdd_probe_file(fd, &io_opts[i], retry_count, timeout); if (devs[i] == NULL) { error = 1; goto bailout; } break; } default: warnx("Unknown device type %d (%s)", io_opts[i].dev_type, io_opts[i].dev_name); error = 1; goto bailout; break; /*NOTREACHED */ } devs[i]->write_dev = io_opts[i].write_dev; devs[i]->start_offset_bytes = io_opts[i].offset; if (max_io != 0) { devs[i]->sector_io_limit = (devs[i]->start_offset_bytes / devs[i]->sector_size) + (max_io / devs[i]->sector_size) - 1; devs[i]->sector_io_limit = (devs[i]->start_offset_bytes / devs[i]->sector_size) + (max_io / devs[i]->sector_size) - 1; } devs[i]->next_io_pos_bytes = devs[i]->start_offset_bytes; devs[i]->next_completion_pos_bytes =devs[i]->start_offset_bytes; } devs[0]->peer_dev = devs[1]; devs[1]->peer_dev = devs[0]; devs[0]->next_peer_pos_bytes = devs[0]->peer_dev->next_io_pos_bytes; devs[1]->next_peer_pos_bytes = devs[1]->peer_dev->next_io_pos_bytes; sem_init(&camdd_sem, /*pshared*/ 0, 0); signal(SIGINFO, camdd_sig_handler); signal(SIGINT, camdd_sig_handler); error = clock_gettime(CLOCK_MONOTONIC_PRECISE, &start_time); if (error != 0) { warn("Unable to get start time"); goto bailout; } for (i = 0; i < num_io_opts; i++) { error = pthread_create(&threads[i], NULL, camdd_worker, (void *)devs[i]); if (error != 0) { warnc(error, "pthread_create() failed"); goto bailout; } } for (;;) { if ((sem_wait(&camdd_sem) == -1) || (need_exit != 0)) { struct kevent ke; for (i = 0; i < num_io_opts; i++) { EV_SET(&ke, (uintptr_t)&devs[i]->work_queue, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); devs[i]->flags |= CAMDD_DEV_FLAG_EOF; error = kevent(devs[i]->kq, &ke, 1, NULL, 0, NULL); if (error == -1) warn("%s: unable to wake up thread", __func__); error = 0; } break; } else if (need_status != 0) { camdd_print_status(devs[0], devs[1], &start_time); need_status = 0; } } for (i = 0; i < num_io_opts; i++) { pthread_join(threads[i], NULL); } camdd_print_status(devs[0], devs[1], &start_time); bailout: for (i = 0; i < num_io_opts; i++) camdd_free_dev(devs[i]); return (error + error_exit); } void usage(void) { fprintf(stderr, "usage: camdd <-i|-o pass=pass0,bs=1M,offset=1M,depth=4>\n" " <-i|-o file=/tmp/file,bs=512K,offset=1M>\n" " <-i|-o file=/dev/da0,bs=512K,offset=1M>\n" " <-i|-o file=/dev/nsa0,bs=512K>\n" " [-C retry_count][-E][-m max_io_amt][-t timeout_secs][-v][-h]\n" "Option description\n" "-i Specify input device/file and parameters\n" "-o Specify output device/file and parameters\n" "Input and Output parameters\n" "pass=name Specify a pass(4) device like pass0 or /dev/pass0\n" "file=name Specify a file or device, /tmp/foo, /dev/da0, /dev/null\n" " or - for stdin/stdout\n" "bs=blocksize Specify blocksize in bytes, or using K, M, G, etc. suffix\n" "offset=len Specify starting offset in bytes or using K, M, G suffix\n" " NOTE: offset cannot be specified on tapes, pipes, stdin/out\n" "depth=N Specify a numeric queue depth. This only applies to pass(4)\n" "mcs=N Specify a minimum cmd size for pass(4) read/write commands\n" "Optional arguments\n" "-C retry_cnt Specify a retry count for pass(4) devices\n" "-E Enable CAM error recovery for pass(4) devices\n" "-m max_io Specify the maximum amount to be transferred in bytes or\n" " using K, G, M, etc. suffixes\n" "-t timeout Specify the I/O timeout to use with pass(4) devices\n" "-v Enable verbose error recovery\n" "-h Print this message\n"); } int camdd_parse_io_opts(char *args, int is_write, struct camdd_io_opts *io_opts) { char *tmpstr, *tmpstr2; char *orig_tmpstr = NULL; int retval = 0; io_opts->write_dev = is_write; tmpstr = strdup(args); if (tmpstr == NULL) { warn("strdup failed"); retval = 1; goto bailout; } orig_tmpstr = tmpstr; while ((tmpstr2 = strsep(&tmpstr, ",")) != NULL) { char *name, *value; /* * If the user creates an empty parameter by putting in two * commas, skip over it and look for the next field. */ if (*tmpstr2 == '\0') continue; name = strsep(&tmpstr2, "="); if (*name == '\0') { warnx("Got empty I/O parameter name"); retval = 1; goto bailout; } value = strsep(&tmpstr2, "="); if ((value == NULL) || (*value == '\0')) { warnx("Empty I/O parameter value for %s", name); retval = 1; goto bailout; } if (strncasecmp(name, "file", 4) == 0) { io_opts->dev_type = CAMDD_DEV_FILE; io_opts->dev_name = strdup(value); if (io_opts->dev_name == NULL) { warn("Error allocating memory"); retval = 1; goto bailout; } } else if (strncasecmp(name, "pass", 4) == 0) { io_opts->dev_type = CAMDD_DEV_PASS; io_opts->dev_name = strdup(value); if (io_opts->dev_name == NULL) { warn("Error allocating memory"); retval = 1; goto bailout; } } else if ((strncasecmp(name, "bs", 2) == 0) || (strncasecmp(name, "blocksize", 9) == 0)) { retval = expand_number(value, &io_opts->blocksize); if (retval == -1) { warn("expand_number(3) failed on %s=%s", name, value); retval = 1; goto bailout; } } else if (strncasecmp(name, "depth", 5) == 0) { char *endptr; io_opts->queue_depth = strtoull(value, &endptr, 0); if (*endptr != '\0') { warnx("invalid queue depth %s", value); retval = 1; goto bailout; } } else if (strncasecmp(name, "mcs", 3) == 0) { char *endptr; io_opts->min_cmd_size = strtol(value, &endptr, 0); if ((*endptr != '\0') || ((io_opts->min_cmd_size > 16) || (io_opts->min_cmd_size < 0))) { warnx("invalid minimum cmd size %s", value); retval = 1; goto bailout; } } else if (strncasecmp(name, "offset", 6) == 0) { retval = expand_number(value, &io_opts->offset); if (retval == -1) { warn("expand_number(3) failed on %s=%s", name, value); retval = 1; goto bailout; } } else if (strncasecmp(name, "debug", 5) == 0) { char *endptr; io_opts->debug = strtoull(value, &endptr, 0); if (*endptr != '\0') { warnx("invalid debug level %s", value); retval = 1; goto bailout; } } else { warnx("Unrecognized parameter %s=%s", name, value); } } bailout: free(orig_tmpstr); return (retval); } int main(int argc, char **argv) { int c; camdd_argmask arglist = CAMDD_ARG_NONE; int timeout = 0, retry_count = 1; int error = 0; uint64_t max_io = 0; struct camdd_io_opts *opt_list = NULL; if (argc == 1) { usage(); exit(1); } opt_list = calloc(2, sizeof(struct camdd_io_opts)); if (opt_list == NULL) { warn("Unable to allocate option list"); error = 1; goto bailout; } while ((c = getopt(argc, argv, "C:Ehi:m:o:t:v")) != -1){ switch (c) { case 'C': retry_count = strtol(optarg, NULL, 0); if (retry_count < 0) errx(1, "retry count %d is < 0", retry_count); arglist |= CAMDD_ARG_RETRIES; break; case 'E': arglist |= CAMDD_ARG_ERR_RECOVER; break; case 'i': case 'o': if (((c == 'i') && (opt_list[0].dev_type != CAMDD_DEV_NONE)) || ((c == 'o') && (opt_list[1].dev_type != CAMDD_DEV_NONE))) { errx(1, "Only one input and output path " "allowed"); } error = camdd_parse_io_opts(optarg, (c == 'o') ? 1 : 0, (c == 'o') ? &opt_list[1] : &opt_list[0]); if (error != 0) goto bailout; break; case 'm': error = expand_number(optarg, &max_io); if (error == -1) { warn("invalid maximum I/O amount %s", optarg); error = 1; goto bailout; } break; case 't': timeout = strtol(optarg, NULL, 0); if (timeout < 0) errx(1, "invalid timeout %d", timeout); /* Convert the timeout from seconds to ms */ timeout *= 1000; arglist |= CAMDD_ARG_TIMEOUT; break; case 'v': arglist |= CAMDD_ARG_VERBOSE; break; case 'h': default: usage(); exit(1); break; /*NOTREACHED*/ } } if ((opt_list[0].dev_type == CAMDD_DEV_NONE) || (opt_list[1].dev_type == CAMDD_DEV_NONE)) errx(1, "Must specify both -i and -o"); /* * Set the timeout if the user hasn't specified one. */ if (timeout == 0) timeout = CAMDD_PASS_RW_TIMEOUT; error = camdd_rw(opt_list, 2, max_io, retry_count, timeout); bailout: free(opt_list); exit(error); } Index: projects/clang380-import =================================================================== --- projects/clang380-import (revision 293015) +++ projects/clang380-import (revision 293016) Property changes on: projects/clang380-import ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r292951-293015