Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PDESC_ZERO.badlib.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PDESC_ZERO.badlib.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PDESC_ZERO.badlib.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_FUNC.badfunc.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_FUNC.badfunc.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_FUNC.badfunc.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_LIB.libdash.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_LIB.libdash.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_LIB.libdash.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.alldash.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.alldash.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.alldash.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.badname.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.badname.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.badname.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.globdash.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.globdash.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_NAME.globdash.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_OFF.toobig.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_OFF.toobig.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/err.D_PROC_OFF.toobig.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" sleep 1000000 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.coverage.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.coverage.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.coverage.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/usr/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" while true; do env > /dev/null; done Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.emptystack.exe =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.emptystack.exe (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/pid/tst.emptystack.exe (revision 278196) @@ -1,29 +1,29 @@ -#!/usr/bin/ksh +#!/usr/bin/env ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" exec find / > /dev/null 2>&1 Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/plockstat/tst.available.exe =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/plockstat/tst.available.exe ___________________________________________________________________ Modified: svn:mime-type ## -1 +1 ## -application/octet-stream \ No newline at end of property +text/plain \ No newline at end of property Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/plockstat/tst.libmap.exe =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/plockstat/tst.libmap.exe ___________________________________________________________________ Modified: svn:mime-type ## -1 +1 ## -application/octet-stream \ No newline at end of property +text/plain \ No newline at end of property Index: projects/building-blocks/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris/lib/libdtrace/common/dt_consume.c (revision 278196) @@ -1,3384 +1,3387 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #ifdef illumos #include #endif #include #include #ifndef illumos #include #endif #define DT_MASK_LO 0x00000000FFFFFFFFULL /* * We declare this here because (1) we need it and (2) we want to avoid a * dependency on libm in libdtrace. */ static long double dt_fabsl(long double x) { if (x < 0) return (-x); return (x); } static int dt_ndigits(long long val) { int rval = 1; long long cmp = 10; if (val < 0) { val = val == INT64_MIN ? INT64_MAX : -val; rval++; } while (val > cmp && cmp > 0) { rval++; cmp *= 10; } return (rval < 4 ? 4 : rval); } /* * 128-bit arithmetic functions needed to support the stddev() aggregating * action. */ static int dt_gt_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] > b[0])); } static int dt_ge_128(uint64_t *a, uint64_t *b) { return (a[1] > b[1] || (a[1] == b[1] && a[0] >= b[0])); } static int dt_le_128(uint64_t *a, uint64_t *b) { return (a[1] < b[1] || (a[1] == b[1] && a[0] <= b[0])); } /* * Shift the 128-bit value in a by b. If b is positive, shift left. * If b is negative, shift right. */ static void dt_shift_128(uint64_t *a, int b) { uint64_t mask; if (b == 0) return; if (b < 0) { b = -b; if (b >= 64) { a[0] = a[1] >> (b - 64); a[1] = 0; } else { a[0] >>= b; mask = 1LL << (64 - b); mask -= 1; a[0] |= ((a[1] & mask) << (64 - b)); a[1] >>= b; } } else { if (b >= 64) { a[1] = a[0] << (b - 64); a[0] = 0; } else { a[1] <<= b; mask = a[0] >> (64 - b); a[1] |= mask; a[0] <<= b; } } } static int dt_nbits_128(uint64_t *a) { int nbits = 0; uint64_t tmp[2]; uint64_t zero[2] = { 0, 0 }; tmp[0] = a[0]; tmp[1] = a[1]; dt_shift_128(tmp, -1); while (dt_gt_128(tmp, zero)) { dt_shift_128(tmp, -1); nbits++; } return (nbits); } static void dt_subtract_128(uint64_t *minuend, uint64_t *subtrahend, uint64_t *difference) { uint64_t result[2]; result[0] = minuend[0] - subtrahend[0]; result[1] = minuend[1] - subtrahend[1] - (minuend[0] < subtrahend[0] ? 1 : 0); difference[0] = result[0]; difference[1] = result[1]; } static void dt_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) { uint64_t result[2]; result[0] = addend1[0] + addend2[0]; result[1] = addend1[1] + addend2[1] + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); sum[0] = result[0]; sum[1] = result[1]; } /* * The basic idea is to break the 2 64-bit values into 4 32-bit values, * use native multiplication on those, and then re-combine into the * resulting 128-bit value. * * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = * hi1 * hi2 << 64 + * hi1 * lo2 << 32 + * hi2 * lo1 << 32 + * lo1 * lo2 */ static void dt_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) { uint64_t hi1, hi2, lo1, lo2; uint64_t tmp[2]; hi1 = factor1 >> 32; hi2 = factor2 >> 32; lo1 = factor1 & DT_MASK_LO; lo2 = factor2 & DT_MASK_LO; product[0] = lo1 * lo2; product[1] = hi1 * hi2; tmp[0] = hi1 * lo2; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); tmp[0] = hi2 * lo1; tmp[1] = 0; dt_shift_128(tmp, 32); dt_add_128(product, tmp, product); } /* * This is long-hand division. * * We initialize subtrahend by shifting divisor left as far as possible. We * loop, comparing subtrahend to dividend: if subtrahend is smaller, we * subtract and set the appropriate bit in the result. We then shift * subtrahend right by one bit for the next comparison. */ static void dt_divide_128(uint64_t *dividend, uint64_t divisor, uint64_t *quotient) { uint64_t result[2] = { 0, 0 }; uint64_t remainder[2]; uint64_t subtrahend[2]; uint64_t divisor_128[2]; uint64_t mask[2] = { 1, 0 }; int log = 0; assert(divisor != 0); divisor_128[0] = divisor; divisor_128[1] = 0; remainder[0] = dividend[0]; remainder[1] = dividend[1]; subtrahend[0] = divisor; subtrahend[1] = 0; while (divisor > 0) { log++; divisor >>= 1; } dt_shift_128(subtrahend, 128 - log); dt_shift_128(mask, 128 - log); while (dt_ge_128(remainder, divisor_128)) { if (dt_ge_128(remainder, subtrahend)) { dt_subtract_128(remainder, subtrahend, remainder); result[0] |= mask[0]; result[1] |= mask[1]; } dt_shift_128(subtrahend, -1); dt_shift_128(mask, -1); } quotient[0] = result[0]; quotient[1] = result[1]; } /* * This is the long-hand method of calculating a square root. * The algorithm is as follows: * * 1. Group the digits by 2 from the right. * 2. Over the leftmost group, find the largest single-digit number * whose square is less than that group. * 3. Subtract the result of the previous step (2 or 4, depending) and * bring down the next two-digit group. * 4. For the result R we have so far, find the largest single-digit number * x such that 2 * R * 10 * x + x^2 is less than the result from step 3. * (Note that this is doubling R and performing a decimal left-shift by 1 * and searching for the appropriate decimal to fill the one's place.) * The value x is the next digit in the square root. * Repeat steps 3 and 4 until the desired precision is reached. (We're * dealing with integers, so the above is sufficient.) * * In decimal, the square root of 582,734 would be calculated as so: * * __7__6__3 * | 58 27 34 * -49 (7^2 == 49 => 7 is the first digit in the square root) * -- * 9 27 (Subtract and bring down the next group.) * 146 8 76 (2 * 7 * 10 * 6 + 6^2 == 876 => 6 is the next digit in * ----- the square root) * 51 34 (Subtract and bring down the next group.) * 1523 45 69 (2 * 76 * 10 * 3 + 3^2 == 4569 => 3 is the next digit in * ----- the square root) * 5 65 (remainder) * * The above algorithm applies similarly in binary, but note that the * only possible non-zero value for x in step 4 is 1, so step 4 becomes a * simple decision: is 2 * R * 2 * 1 + 1^2 (aka R << 2 + 1) less than the * preceding difference? * * In binary, the square root of 11011011 would be calculated as so: * * __1__1__1__0 * | 11 01 10 11 * 01 (0 << 2 + 1 == 1 < 11 => this bit is 1) * -- * 10 01 10 11 * 101 1 01 (1 << 2 + 1 == 101 < 1001 => next bit is 1) * ----- * 1 00 10 11 * 1101 11 01 (11 << 2 + 1 == 1101 < 10010 => next bit is 1) * ------- * 1 01 11 * 11101 1 11 01 (111 << 2 + 1 == 11101 > 10111 => last bit is 0) * */ static uint64_t dt_sqrt_128(uint64_t *square) { uint64_t result[2] = { 0, 0 }; uint64_t diff[2] = { 0, 0 }; uint64_t one[2] = { 1, 0 }; uint64_t next_pair[2]; uint64_t next_try[2]; uint64_t bit_pairs, pair_shift; int i; bit_pairs = dt_nbits_128(square) / 2; pair_shift = bit_pairs * 2; for (i = 0; i <= bit_pairs; i++) { /* * Bring down the next pair of bits. */ next_pair[0] = square[0]; next_pair[1] = square[1]; dt_shift_128(next_pair, -pair_shift); next_pair[0] &= 0x3; next_pair[1] = 0; dt_shift_128(diff, 2); dt_add_128(diff, next_pair, diff); /* * next_try = R << 2 + 1 */ next_try[0] = result[0]; next_try[1] = result[1]; dt_shift_128(next_try, 2); dt_add_128(next_try, one, next_try); if (dt_le_128(next_try, diff)) { dt_subtract_128(diff, next_try, diff); dt_shift_128(result, 1); dt_add_128(result, one, result); } else { dt_shift_128(result, 1); } pair_shift -= 2; } assert(result[1] == 0); return (result[0]); } uint64_t dt_stddev(uint64_t *data, uint64_t normal) { uint64_t avg_of_squares[2]; uint64_t square_of_avg[2]; int64_t norm_avg; uint64_t diff[2]; + if (data[0] == 0) + return (0); + /* * The standard approximation for standard deviation is * sqrt(average(x**2) - average(x)**2), i.e. the square root * of the average of the squares minus the square of the average. */ dt_divide_128(data + 2, normal, avg_of_squares); dt_divide_128(avg_of_squares, data[0], avg_of_squares); norm_avg = (int64_t)data[1] / (int64_t)normal / (int64_t)data[0]; if (norm_avg < 0) norm_avg = -norm_avg; dt_multiply_128((uint64_t)norm_avg, (uint64_t)norm_avg, square_of_avg); dt_subtract_128(avg_of_squares, square_of_avg, diff); return (dt_sqrt_128(diff)); } static int dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last, dtrace_bufdesc_t *buf, size_t offs) { dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd; dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd; char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub; dtrace_flowkind_t flow = DTRACEFLOW_NONE; const char *str = NULL; static const char *e_str[2] = { " -> ", " => " }; static const char *r_str[2] = { " <- ", " <= " }; static const char *ent = "entry", *ret = "return"; static int entlen = 0, retlen = 0; dtrace_epid_t next, id = epd->dtepd_epid; int rval; if (entlen == 0) { assert(retlen == 0); entlen = strlen(ent); retlen = strlen(ret); } /* * If the name of the probe is "entry" or ends with "-entry", we * treat it as an entry; if it is "return" or ends with "-return", * we treat it as a return. (This allows application-provided probes * like "method-entry" or "function-entry" to participate in flow * indentation -- without accidentally misinterpreting popular probe * names like "carpentry", "gentry" or "Coventry".) */ if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_ENTRY; str = e_str[strcmp(p, "syscall") == 0]; } else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' && (sub == n || sub[-1] == '-')) { flow = DTRACEFLOW_RETURN; str = r_str[strcmp(p, "syscall") == 0]; } /* * If we're going to indent this, we need to check the ID of our last * call. If we're looking at the same probe ID but a different EPID, * we _don't_ want to indent. (Yes, there are some minor holes in * this scheme -- it's a heuristic.) */ if (flow == DTRACEFLOW_ENTRY) { if ((last != DTRACE_EPIDNONE && id != last && pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id)) flow = DTRACEFLOW_NONE; } /* * If we're going to unindent this, it's more difficult to see if * we don't actually want to unindent it -- we need to look at the * _next_ EPID. */ if (flow == DTRACEFLOW_RETURN) { offs += epd->dtepd_size; do { if (offs >= buf->dtbd_size) goto out; next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (next == DTRACE_EPIDNONE) offs += sizeof (id); } while (next == DTRACE_EPIDNONE); if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0) return (rval); if (next != id && npd->dtpd_id == pd->dtpd_id) flow = DTRACEFLOW_NONE; } out: if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) { data->dtpda_prefix = str; } else { data->dtpda_prefix = "| "; } if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0) data->dtpda_indent -= 2; data->dtpda_flow = flow; return (0); } static int dt_nullprobe() { return (DTRACE_CONSUME_THIS); } static int dt_nullrec() { return (DTRACE_CONSUME_NEXT); } static void dt_quantize_total(dtrace_hdl_t *dtp, int64_t datum, long double *total) { long double val = dt_fabsl((long double)datum); if (dtp->dt_options[DTRACEOPT_AGGZOOM] == DTRACEOPT_UNSET) { *total += val; return; } /* * If we're zooming in on an aggregation, we want the height of the * highest value to be approximately 95% of total bar height -- so we * adjust up by the reciprocal of DTRACE_AGGZOOM_MAX when comparing to * our highest value. */ val *= 1 / DTRACE_AGGZOOM_MAX; if (*total < val) *total = val; } static int dt_print_quanthdr(dtrace_hdl_t *dtp, FILE *fp, int width) { return (dt_printf(dtp, fp, "\n%*s %41s %-9s\n", width ? width : 16, width ? "key" : "value", "------------- Distribution -------------", "count")); } static int dt_print_quanthdr_packed(dtrace_hdl_t *dtp, FILE *fp, int width, const dtrace_aggdata_t *aggdata, dtrace_actkind_t action) { int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin; int minwidth, maxwidth, i; assert(action == DTRACEAGG_QUANTIZE || action == DTRACEAGG_LQUANTIZE); if (action == DTRACEAGG_QUANTIZE) { if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) min--; if (max < DTRACE_QUANTIZE_NBUCKETS - 1) max++; minwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(min)); maxwidth = dt_ndigits(DTRACE_QUANTIZE_BUCKETVAL(max)); } else { maxwidth = 8; minwidth = maxwidth - 1; max++; } if (dt_printf(dtp, fp, "\n%*s %*s .", width, width > 0 ? "key" : "", minwidth, "min") < 0) return (-1); for (i = min; i <= max; i++) { if (dt_printf(dtp, fp, "-") < 0) return (-1); } return (dt_printf(dtp, fp, ". %*s | count\n", -maxwidth, "max")); } /* * We use a subset of the Unicode Block Elements (U+2588 through U+258F, * inclusive) to represent aggregations via UTF-8 -- which are expressed via * 3-byte UTF-8 sequences. */ #define DTRACE_AGGUTF8_FULL 0x2588 #define DTRACE_AGGUTF8_BASE 0x258f #define DTRACE_AGGUTF8_LEVELS 8 #define DTRACE_AGGUTF8_BYTE0(val) (0xe0 | ((val) >> 12)) #define DTRACE_AGGUTF8_BYTE1(val) (0x80 | (((val) >> 6) & 0x3f)) #define DTRACE_AGGUTF8_BYTE2(val) (0x80 | ((val) & 0x3f)) static int dt_print_quantline_utf8(dtrace_hdl_t *dtp, FILE *fp, int64_t val, uint64_t normal, long double total) { uint_t len = 40, i, whole, partial; long double f = (dt_fabsl((long double)val) * len) / total; const char *spaces = " "; whole = (uint_t)f; partial = (uint_t)((f - (long double)(uint_t)f) * (long double)DTRACE_AGGUTF8_LEVELS); if (dt_printf(dtp, fp, "|") < 0) return (-1); for (i = 0; i < whole; i++) { if (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(DTRACE_AGGUTF8_FULL), DTRACE_AGGUTF8_BYTE1(DTRACE_AGGUTF8_FULL), DTRACE_AGGUTF8_BYTE2(DTRACE_AGGUTF8_FULL)) < 0) return (-1); } if (partial != 0) { partial = DTRACE_AGGUTF8_BASE - (partial - 1); if (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(partial), DTRACE_AGGUTF8_BYTE1(partial), DTRACE_AGGUTF8_BYTE2(partial)) < 0) return (-1); i++; } return (dt_printf(dtp, fp, "%s %-9lld\n", spaces + i, (long long)val / normal)); } static int dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val, uint64_t normal, long double total, char positives, char negatives) { long double f; uint_t depth, len = 40; const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"; const char *spaces = " "; assert(strlen(ats) == len && strlen(spaces) == len); assert(!(total == 0 && (positives || negatives))); assert(!(val < 0 && !negatives)); assert(!(val > 0 && !positives)); assert(!(val != 0 && total == 0)); if (!negatives) { if (positives) { if (dtp->dt_encoding == DT_ENCODING_UTF8) { return (dt_print_quantline_utf8(dtp, fp, val, normal, total)); } f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); } else { depth = 0; } return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth, spaces + depth, (long long)val / normal)); } if (!positives) { f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth, ats + len - depth, (long long)val / normal)); } /* * If we're here, we have both positive and negative bucket values. * To express this graphically, we're going to generate both positive * and negative bars separated by a centerline. These bars are half * the size of normal quantize()/lquantize() bars, so we divide the * length in half before calculating the bar length. */ len /= 2; ats = &ats[len]; spaces = &spaces[len]; f = (dt_fabsl((long double)val) * len) / total; depth = (uint_t)(f + 0.5); if (val <= 0) { return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth, ats + len - depth, len, "", (long long)val / normal)); } else { return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "", ats + len - depth, spaces + depth, (long long)val / normal)); } } /* * As with UTF-8 printing of aggregations, we use a subset of the Unicode * Block Elements (U+2581 through U+2588, inclusive) to represent our packed * aggregation. */ #define DTRACE_AGGPACK_BASE 0x2581 #define DTRACE_AGGPACK_LEVELS 8 static int dt_print_packed(dtrace_hdl_t *dtp, FILE *fp, long double datum, long double total) { static boolean_t utf8_checked = B_FALSE; static boolean_t utf8; char *ascii = "__xxxxXX"; char *neg = "vvvvVV"; unsigned int len; long double val; if (!utf8_checked) { char *term; /* * We want to determine if we can reasonably emit UTF-8 for our * packed aggregation. To do this, we will check for terminals * that are known to be primitive to emit UTF-8 on these. */ utf8_checked = B_TRUE; if (dtp->dt_encoding == DT_ENCODING_ASCII) { utf8 = B_FALSE; } else if (dtp->dt_encoding == DT_ENCODING_UTF8) { utf8 = B_TRUE; } else if ((term = getenv("TERM")) != NULL && (strcmp(term, "sun") == 0 || strcmp(term, "sun-color") == 0) || strcmp(term, "dumb") == 0) { utf8 = B_FALSE; } else { utf8 = B_TRUE; } } if (datum == 0) return (dt_printf(dtp, fp, " ")); if (datum < 0) { len = strlen(neg); val = dt_fabsl(datum * (len - 1)) / total; return (dt_printf(dtp, fp, "%c", neg[(uint_t)(val + 0.5)])); } if (utf8) { int block = DTRACE_AGGPACK_BASE + (unsigned int)(((datum * (DTRACE_AGGPACK_LEVELS - 1)) / total) + 0.5); return (dt_printf(dtp, fp, "%c%c%c", DTRACE_AGGUTF8_BYTE0(block), DTRACE_AGGUTF8_BYTE1(block), DTRACE_AGGUTF8_BYTE2(block))); } len = strlen(ascii); val = (datum * (len - 1)) / total; return (dt_printf(dtp, fp, "%c", ascii[(uint_t)(val + 0.5)])); } int dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { const int64_t *data = addr; int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; long double total = 0; char positives = 0, negatives = 0; if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) first_bin++; if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { /* * There isn't any data. This is possible if the aggregation * has been clear()'d or if negative increment values have been * used. Regardless, we'll print the buckets around 0. */ first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_print_quanthdr(dtp, fp, 0) < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { if (dt_printf(dtp, fp, "%16lld ", (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } int dt_print_quantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, const dtrace_aggdata_t *aggdata) { const int64_t *data = addr; long double total = 0, count = 0; int min = aggdata->dtada_minbin, max = aggdata->dtada_maxbin, i; int64_t minval, maxval; if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); if (min != 0 && min != DTRACE_QUANTIZE_ZEROBUCKET) min--; if (max < DTRACE_QUANTIZE_NBUCKETS - 1) max++; minval = DTRACE_QUANTIZE_BUCKETVAL(min); maxval = DTRACE_QUANTIZE_BUCKETVAL(max); if (dt_printf(dtp, fp, " %*lld :", dt_ndigits(minval), (long long)minval) < 0) return (-1); for (i = min; i <= max; i++) { dt_quantize_total(dtp, data[i], &total); count += data[i]; } for (i = min; i <= max; i++) { if (dt_print_packed(dtp, fp, data[i], total) < 0) return (-1); } if (dt_printf(dtp, fp, ": %*lld | %lld\n", -dt_ndigits(maxval), (long long)maxval, (long long)count) < 0) return (-1); return (0); } int dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { const int64_t *data = addr; int i, first_bin, last_bin, base; uint64_t arg; long double total = 0; uint16_t step, levels; char positives = 0, negatives = 0; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); base = DTRACE_LQUANTIZE_BASE(arg); step = DTRACE_LQUANTIZE_STEP(arg); levels = DTRACE_LQUANTIZE_LEVELS(arg); first_bin = 0; last_bin = levels + 1; if (size != sizeof (uint64_t) * (levels + 2)) return (dt_set_errno(dtp, EDT_DMISMATCH)); while (first_bin <= levels + 1 && data[first_bin] == 0) first_bin++; if (first_bin > levels + 1) { first_bin = 0; last_bin = 2; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < levels + 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); for (i = first_bin; i <= last_bin; i++) { char c[32]; int err; if (i == 0) { (void) snprintf(c, sizeof (c), "< %d", base); err = dt_printf(dtp, fp, "%16s ", c); } else if (i == levels + 1) { (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); err = dt_printf(dtp, fp, "%16s ", c); } else { err = dt_printf(dtp, fp, "%16d ", base + (i - 1) * step); } if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal, total, positives, negatives) < 0) return (-1); } return (0); } /*ARGSUSED*/ int dt_print_lquantize_packed(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, const dtrace_aggdata_t *aggdata) { const int64_t *data = addr; long double total = 0, count = 0; int min, max, base, err; uint64_t arg; uint16_t step, levels; char c[32]; unsigned int i; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); base = DTRACE_LQUANTIZE_BASE(arg); step = DTRACE_LQUANTIZE_STEP(arg); levels = DTRACE_LQUANTIZE_LEVELS(arg); if (size != sizeof (uint64_t) * (levels + 2)) return (dt_set_errno(dtp, EDT_DMISMATCH)); min = 0; max = levels + 1; if (min == 0) { (void) snprintf(c, sizeof (c), "< %d", base); err = dt_printf(dtp, fp, "%8s :", c); } else { err = dt_printf(dtp, fp, "%8d :", base + (min - 1) * step); } if (err < 0) return (-1); for (i = min; i <= max; i++) { dt_quantize_total(dtp, data[i], &total); count += data[i]; } for (i = min; i <= max; i++) { if (dt_print_packed(dtp, fp, data[i], total) < 0) return (-1); } (void) snprintf(c, sizeof (c), ">= %d", base + (levels * step)); return (dt_printf(dtp, fp, ": %-8s | %lld\n", c, (long long)count)); } int dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size, uint64_t normal) { int i, first_bin, last_bin, bin = 1, order, levels; uint16_t factor, low, high, nsteps; const int64_t *data = addr; int64_t value = 1, next, step; char positives = 0, negatives = 0; long double total = 0; uint64_t arg; char c[32]; if (size < sizeof (uint64_t)) return (dt_set_errno(dtp, EDT_DMISMATCH)); arg = *data++; size -= sizeof (uint64_t); factor = DTRACE_LLQUANTIZE_FACTOR(arg); low = DTRACE_LLQUANTIZE_LOW(arg); high = DTRACE_LLQUANTIZE_HIGH(arg); nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); /* * We don't expect to be handed invalid llquantize() parameters here, * but sanity check them (to a degree) nonetheless. */ if (size > INT32_MAX || factor < 2 || low >= high || nsteps == 0 || factor > nsteps) return (dt_set_errno(dtp, EDT_DMISMATCH)); levels = (int)size / sizeof (uint64_t); first_bin = 0; last_bin = levels - 1; while (first_bin < levels && data[first_bin] == 0) first_bin++; if (first_bin == levels) { first_bin = 0; last_bin = 1; } else { if (first_bin > 0) first_bin--; while (last_bin > 0 && data[last_bin] == 0) last_bin--; if (last_bin < levels - 1) last_bin++; } for (i = first_bin; i <= last_bin; i++) { positives |= (data[i] > 0); negatives |= (data[i] < 0); dt_quantize_total(dtp, data[i], &total); } if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", "------------- Distribution -------------", "count") < 0) return (-1); for (order = 0; order < low; order++) value *= factor; next = value * factor; step = next > nsteps ? next / nsteps : 1; if (first_bin == 0) { (void) snprintf(c, sizeof (c), "< %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[0], normal, total, positives, negatives) < 0) return (-1); } while (order <= high) { if (bin >= first_bin && bin <= last_bin) { if (dt_printf(dtp, fp, "%16lld ", (long long)value) < 0) return (-1); if (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives) < 0) return (-1); } assert(value < next); bin++; if ((value += step) != next) continue; next = value * factor; step = next > nsteps ? next / nsteps : 1; order++; } if (last_bin < bin) return (0); assert(last_bin == bin); (void) snprintf(c, sizeof (c), ">= %lld", (long long)value); if (dt_printf(dtp, fp, "%16s ", c) < 0) return (-1); return (dt_print_quantline(dtp, fp, data[bin], normal, total, positives, negatives)); } /*ARGSUSED*/ static int dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size, uint64_t normal) { /* LINTED - alignment */ int64_t *data = (int64_t *)addr; return (dt_printf(dtp, fp, " %16lld", data[0] ? (long long)(data[1] / (int64_t)normal / data[0]) : 0)); } /*ARGSUSED*/ static int dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t size, uint64_t normal) { /* LINTED - alignment */ uint64_t *data = (uint64_t *)addr; return (dt_printf(dtp, fp, " %16llu", data[0] ? (unsigned long long) dt_stddev(data, normal) : 0)); } /*ARGSUSED*/ static int dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, size_t nbytes, int width, int quiet, int forceraw) { /* * If the byte stream is a series of printable characters, followed by * a terminating byte, we print it out as a string. Otherwise, we * assume that it's something else and just print the bytes. */ int i, j, margin = 5; char *c = (char *)addr; if (nbytes == 0) return (0); if (forceraw) goto raw; if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) goto raw; for (i = 0; i < nbytes; i++) { /* * We define a "printable character" to be one for which * isprint(3C) returns non-zero, isspace(3C) returns non-zero, * or a character which is either backspace or the bell. * Backspace and the bell are regrettably special because * they fail the first two tests -- and yet they are entirely * printable. These are the only two control characters that * have meaning for the terminal and for which isprint(3C) and * isspace(3C) return 0. */ if (isprint(c[i]) || isspace(c[i]) || c[i] == '\b' || c[i] == '\a') continue; if (c[i] == '\0' && i > 0) { /* * This looks like it might be a string. Before we * assume that it is indeed a string, check the * remainder of the byte range; if it contains * additional non-nul characters, we'll assume that * it's a binary stream that just happens to look like * a string, and we'll print out the individual bytes. */ for (j = i + 1; j < nbytes; j++) { if (c[j] != '\0') break; } if (j != nbytes) break; if (quiet) { return (dt_printf(dtp, fp, "%s", c)); } else { return (dt_printf(dtp, fp, " %s%*s", width < 0 ? " " : "", width, c)); } } break; } if (i == nbytes) { /* * The byte range is all printable characters, but there is * no trailing nul byte. We'll assume that it's a string and * print it as such. */ char *s = alloca(nbytes + 1); bcopy(c, s, nbytes); s[nbytes] = '\0'; return (dt_printf(dtp, fp, " %-*s", width, s)); } raw: if (dt_printf(dtp, fp, "\n%*s ", margin, "") < 0) return (-1); for (i = 0; i < 16; i++) if (dt_printf(dtp, fp, " %c", "0123456789abcdef"[i]) < 0) return (-1); if (dt_printf(dtp, fp, " 0123456789abcdef\n") < 0) return (-1); for (i = 0; i < nbytes; i += 16) { if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0) return (-1); } while (j++ % 16) { if (dt_printf(dtp, fp, " ") < 0) return (-1); } if (dt_printf(dtp, fp, " ") < 0) return (-1); for (j = i; j < i + 16 && j < nbytes; j++) { if (dt_printf(dtp, fp, "%c", c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0) return (-1); } if (dt_printf(dtp, fp, "\n") < 0) return (-1); } return (0); } int dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr, int depth, int size) { dtrace_syminfo_t dts; GElf_Sym sym; int i, indent; char c[PATH_MAX * 2]; uint64_t pc; if (dt_printf(dtp, fp, "\n") < 0) return (-1); if (format == NULL) format = "%s"; if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; else indent = _dtrace_stkindent; for (i = 0; i < depth; i++) { switch (size) { case sizeof (uint32_t): /* LINTED - alignment */ pc = *((uint32_t *)addr); break; case sizeof (uint64_t): /* LINTED - alignment */ pc = *((uint64_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADSTACKPC)); } if (pc == 0) break; addr += size; if (dt_printf(dtp, fp, "%*s", indent, "") < 0) return (-1); if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { if (pc > sym.st_value) { (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", dts.dts_object, dts.dts_name, (u_longlong_t)(pc - sym.st_value)); } else { (void) snprintf(c, sizeof (c), "%s`%s", dts.dts_object, dts.dts_name); } } else { /* * We'll repeat the lookup, but this time we'll specify * a NULL GElf_Sym -- indicating that we're only * interested in the containing module. */ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dts.dts_object, (u_longlong_t)pc); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } } if (dt_printf(dtp, fp, format, c) < 0) return (-1); if (dt_printf(dtp, fp, "\n") < 0) return (-1); } return (0); } int dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr, uint64_t arg) { /* LINTED - alignment */ uint64_t *pc = (uint64_t *)addr; uint32_t depth = DTRACE_USTACK_NFRAMES(arg); uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); const char *strbase = addr + (depth + 1) * sizeof (uint64_t); const char *str = strsize ? strbase : NULL; int err = 0; char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; GElf_Sym sym; int i, indent; pid_t pid; if (depth == 0) return (0); pid = (pid_t)*pc++; if (dt_printf(dtp, fp, "\n") < 0) return (-1); if (format == NULL) format = "%s"; if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; else indent = _dtrace_stkindent; /* * Ultimately, we need to add an entry point in the library vector for * determining from . For now, if * this is a vector open, we just print the raw address or string. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ for (i = 0; i < depth && pc[i] != 0; i++) { const prmap_t *map; if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) break; if (P != NULL && Plookup_by_addr(P, pc[i], name, sizeof (name), &sym) == 0) { (void) Pobjname(P, pc[i], objname, sizeof (objname)); if (pc[i] > sym.st_value) { (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", dt_basename(objname), name, (u_longlong_t)(pc[i] - sym.st_value)); } else { (void) snprintf(c, sizeof (c), "%s`%s", dt_basename(objname), name); } } else if (str != NULL && str[0] != '\0' && str[0] != '@' && (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || (map->pr_mflags & MA_WRITE)))) { /* * If the current string pointer in the string table * does not point to an empty string _and_ the program * counter falls in a writable region, we'll use the * string from the string table instead of the raw * address. This last condition is necessary because * some (broken) ustack helpers will return a string * even for a program counter that they can't * identify. If we have a string for a program * counter that falls in a segment that isn't * writable, we assume that we have fallen into this * case and we refuse to use the string. */ (void) snprintf(c, sizeof (c), "%s", str); } else { if (P != NULL && Pobjname(P, pc[i], objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dt_basename(objname), (u_longlong_t)pc[i]); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc[i]); } } if ((err = dt_printf(dtp, fp, format, c)) < 0) break; if ((err = dt_printf(dtp, fp, "\n")) < 0) break; if (str != NULL && str[0] == '@') { /* * If the first character of the string is an "at" sign, * then the string is inferred to be an annotation -- * and it is printed out beneath the frame and offset * with brackets. */ if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) break; (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); if ((err = dt_printf(dtp, fp, format, c)) < 0) break; if ((err = dt_printf(dtp, fp, "\n")) < 0) break; } if (str != NULL) { str += strlen(str) + 1; if (str - strbase >= strsize) str = NULL; } } if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } static int dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; const char *format = " %-50s"; char *s; int n, len = 256; if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { struct ps_prochandle *P; if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { GElf_Sym sym; dt_proc_lock(dtp, P); if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) pc = sym.st_value; dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } } do { n = len; s = alloca(n); } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n); return (dt_printf(dtp, fp, format, s)); } int dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pid = ((uint64_t *)addr)[0]; /* LINTED - alignment */ uint64_t pc = ((uint64_t *)addr)[1]; int err = 0; char objname[PATH_MAX], c[PATH_MAX * 2]; struct ps_prochandle *P; if (format == NULL) format = " %-50s"; /* * See the comment in dt_print_ustack() for the rationale for * printing raw addresses in the vectored case. */ if (dtp->dt_vector == NULL) P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); else P = NULL; if (P != NULL) dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { (void) snprintf(c, sizeof (c), "%s", dt_basename(objname)); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } err = dt_printf(dtp, fp, format, c); if (P != NULL) { dt_proc_unlock(dtp, P); dt_proc_release(dtp, P); } return (err); } int dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) { int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); size_t nbytes = *((uintptr_t *) addr); return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), nbytes, 50, quiet, 1)); } typedef struct dt_type_cbdata { dtrace_hdl_t *dtp; dtrace_typeinfo_t dtt; caddr_t addr; caddr_t addrend; const char *name; int f_type; int indent; int type_width; int name_width; FILE *fp; } dt_type_cbdata_t; static int dt_print_type_data(dt_type_cbdata_t *, ctf_id_t); static int dt_print_type_member(const char *name, ctf_id_t type, ulong_t off, void *arg) { dt_type_cbdata_t cbdata; dt_type_cbdata_t *cbdatap = arg; ssize_t ssz; if ((ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type)) <= 0) return (0); off /= 8; cbdata = *cbdatap; cbdata.name = name; cbdata.addr += off; cbdata.addrend = cbdata.addr + ssz; return (dt_print_type_data(&cbdata, type)); } static int dt_print_type_width(const char *name, ctf_id_t type, ulong_t off, void *arg) { char buf[DT_TYPE_NAMELEN]; char *p; dt_type_cbdata_t *cbdatap = arg; size_t sz = strlen(name); ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); if ((p = strchr(buf, '[')) != NULL) p[-1] = '\0'; else p = ""; sz += strlen(p); if (sz > cbdatap->name_width) cbdatap->name_width = sz; sz = strlen(buf); if (sz > cbdatap->type_width) cbdatap->type_width = sz; return (0); } static int dt_print_type_data(dt_type_cbdata_t *cbdatap, ctf_id_t type) { caddr_t addr = cbdatap->addr; caddr_t addrend = cbdatap->addrend; char buf[DT_TYPE_NAMELEN]; char *p; int cnt = 0; uint_t kind = ctf_type_kind(cbdatap->dtt.dtt_ctfp, type); ssize_t ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type); ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); if ((p = strchr(buf, '[')) != NULL) p[-1] = '\0'; else p = ""; if (cbdatap->f_type) { int type_width = roundup(cbdatap->type_width + 1, 4); int name_width = roundup(cbdatap->name_width + 1, 4); name_width -= strlen(cbdatap->name); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s%-*s%s%-*s = ",cbdatap->indent * 4,"",type_width,buf,cbdatap->name,name_width,p); } while (addr < addrend) { dt_type_cbdata_t cbdata; ctf_arinfo_t arinfo; ctf_encoding_t cte; uintptr_t *up; void *vp = addr; cbdata = *cbdatap; cbdata.name = ""; cbdata.addr = addr; cbdata.addrend = addr + ssz; cbdata.f_type = 0; cbdata.indent++; cbdata.type_width = 0; cbdata.name_width = 0; if (cnt > 0) dt_printf(cbdatap->dtp, cbdatap->fp, "%*s", cbdatap->indent * 4,""); switch (kind) { case CTF_K_INTEGER: if (ctf_type_encoding(cbdatap->dtt.dtt_ctfp, type, &cte) != 0) return (-1); if ((cte.cte_format & CTF_INT_SIGNED) != 0) switch (cte.cte_bits) { case 8: if (isprint(*((char *) vp))) dt_printf(cbdatap->dtp, cbdatap->fp, "'%c', ", *((char *) vp)); dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((char *) vp), *((char *) vp)); break; case 16: dt_printf(cbdatap->dtp, cbdatap->fp, "%hd (0x%hx);\n", *((short *) vp), *((u_short *) vp)); break; case 32: dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((int *) vp), *((u_int *) vp)); break; case 64: dt_printf(cbdatap->dtp, cbdatap->fp, "%jd (0x%jx);\n", *((long long *) vp), *((unsigned long long *) vp)); break; default: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; } else switch (cte.cte_bits) { case 8: dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((uint8_t *) vp) & 0xff, *((uint8_t *) vp) & 0xff); break; case 16: dt_printf(cbdatap->dtp, cbdatap->fp, "%hu (0x%hx);\n", *((u_short *) vp), *((u_short *) vp)); break; case 32: dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((u_int *) vp), *((u_int *) vp)); break; case 64: dt_printf(cbdatap->dtp, cbdatap->fp, "%ju (0x%jx);\n", *((unsigned long long *) vp), *((unsigned long long *) vp)); break; default: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; } break; case CTF_K_FLOAT: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FLOAT: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); break; case CTF_K_POINTER: dt_printf(cbdatap->dtp, cbdatap->fp, "%p;\n", *((void **) addr)); break; case CTF_K_ARRAY: if (ctf_array_info(cbdatap->dtt.dtt_ctfp, type, &arinfo) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n%*s",cbdata.indent * 4,""); dt_print_type_data(&cbdata, arinfo.ctr_contents); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_FUNCTION: dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FUNCTION:\n"); break; case CTF_K_STRUCT: cbdata.f_type = 1; if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_width, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_member, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_UNION: cbdata.f_type = 1; if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_width, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, dt_print_type_member, &cbdata) != 0) return (-1); dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); break; case CTF_K_ENUM: dt_printf(cbdatap->dtp, cbdatap->fp, "%s;\n", ctf_enum_name(cbdatap->dtt.dtt_ctfp, type, *((int *) vp))); break; case CTF_K_TYPEDEF: dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_VOLATILE: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "volatile "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_CONST: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "const "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; case CTF_K_RESTRICT: if (cbdatap->f_type) dt_printf(cbdatap->dtp, cbdatap->fp, "restrict "); dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); break; default: break; } addr += ssz; cnt++; } return (0); } static int dt_print_type(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) { caddr_t addrend; char *p; dtrace_typeinfo_t dtt; dt_type_cbdata_t cbdata; int num = 0; int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); ssize_t ssz; if (!quiet) dt_printf(dtp, fp, "\n"); /* Get the total number of bytes of data buffered. */ size_t nbytes = *((uintptr_t *) addr); addr += sizeof(uintptr_t); /* * Get the size of the type so that we can check that it matches * the CTF data we look up and so that we can figure out how many * type elements are buffered. */ size_t typs = *((uintptr_t *) addr); addr += sizeof(uintptr_t); /* * Point to the type string in the buffer. Get it's string * length and round it up to become the offset to the start * of the buffered type data which we would like to be aligned * for easy access. */ char *strp = (char *) addr; int offset = roundup(strlen(strp) + 1, sizeof(uintptr_t)); /* * The type string might have a format such as 'int [20]'. * Check if there is an array dimension present. */ if ((p = strchr(strp, '[')) != NULL) { /* Strip off the array dimension. */ *p++ = '\0'; for (; *p != '\0' && *p != ']'; p++) num = num * 10 + *p - '0'; } else /* No array dimension, so default. */ num = 1; /* Lookup the CTF type from the type string. */ if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, strp, &dtt) < 0) return (-1); /* Offset the buffer address to the start of the data... */ addr += offset; ssz = ctf_type_size(dtt.dtt_ctfp, dtt.dtt_type); if (typs != ssz) { printf("Expected type size from buffer (%lu) to match type size looked up now (%ld)\n", (u_long) typs, (long) ssz); return (-1); } cbdata.dtp = dtp; cbdata.dtt = dtt; cbdata.name = ""; cbdata.addr = addr; cbdata.addrend = addr + nbytes; cbdata.indent = 1; cbdata.f_type = 1; cbdata.type_width = 0; cbdata.name_width = 0; cbdata.fp = fp; return (dt_print_type_data(&cbdata, dtt.dtt_type)); } static int dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pc = *((uint64_t *)addr); dtrace_syminfo_t dts; GElf_Sym sym; char c[PATH_MAX * 2]; if (format == NULL) format = " %-50s"; if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`%s", dts.dts_object, dts.dts_name); } else { /* * We'll repeat the lookup, but this time we'll specify a * NULL GElf_Sym -- indicating that we're only interested in * the containing module. */ if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s`0x%llx", dts.dts_object, (u_longlong_t)pc); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } } if (dt_printf(dtp, fp, format, c) < 0) return (-1); return (0); } int dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) { /* LINTED - alignment */ uint64_t pc = *((uint64_t *)addr); dtrace_syminfo_t dts; char c[PATH_MAX * 2]; if (format == NULL) format = " %-50s"; if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { (void) snprintf(c, sizeof (c), "%s", dts.dts_object); } else { (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); } if (dt_printf(dtp, fp, format, c) < 0) return (-1); return (0); } typedef struct dt_normal { dtrace_aggvarid_t dtnd_id; uint64_t dtnd_normal; } dt_normal_t; static int dt_normalize_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_normal_t *normal = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = normal->dtnd_id; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; return (DTRACE_AGGWALK_NORMALIZE); } static int dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) { dt_normal_t normal; caddr_t addr; /* * We (should) have two records: the aggregation ID followed by the * normalization value. */ addr = base + rec->dtrd_offset; if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) return (dt_set_errno(dtp, EDT_BADNORMAL)); /* LINTED - alignment */ normal.dtnd_id = *((dtrace_aggvarid_t *)addr); rec++; if (rec->dtrd_action != DTRACEACT_LIBACT) return (dt_set_errno(dtp, EDT_BADNORMAL)); if (rec->dtrd_arg != DT_ACT_NORMALIZE) return (dt_set_errno(dtp, EDT_BADNORMAL)); addr = base + rec->dtrd_offset; switch (rec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint64_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint32_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ normal.dtnd_normal = *((uint16_t *)addr); break; case sizeof (uint8_t): normal.dtnd_normal = *((uint8_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADNORMAL)); } (void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal); return (0); } static int dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg) { dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_DENORMALIZE); } static int dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg) { dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_CLEAR); } typedef struct dt_trunc { dtrace_aggvarid_t dttd_id; uint64_t dttd_remaining; } dt_trunc_t; static int dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_trunc_t *trunc = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t id = trunc->dttd_id; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); if (trunc->dttd_remaining == 0) return (DTRACE_AGGWALK_REMOVE); trunc->dttd_remaining--; return (DTRACE_AGGWALK_NEXT); } static int dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) { dt_trunc_t trunc; caddr_t addr; int64_t remaining; int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *); /* * We (should) have two records: the aggregation ID followed by the * number of aggregation entries after which the aggregation is to be * truncated. */ addr = base + rec->dtrd_offset; if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) return (dt_set_errno(dtp, EDT_BADTRUNC)); /* LINTED - alignment */ trunc.dttd_id = *((dtrace_aggvarid_t *)addr); rec++; if (rec->dtrd_action != DTRACEACT_LIBACT) return (dt_set_errno(dtp, EDT_BADTRUNC)); if (rec->dtrd_arg != DT_ACT_TRUNC) return (dt_set_errno(dtp, EDT_BADTRUNC)); addr = base + rec->dtrd_offset; switch (rec->dtrd_size) { case sizeof (uint64_t): /* LINTED - alignment */ remaining = *((int64_t *)addr); break; case sizeof (uint32_t): /* LINTED - alignment */ remaining = *((int32_t *)addr); break; case sizeof (uint16_t): /* LINTED - alignment */ remaining = *((int16_t *)addr); break; case sizeof (uint8_t): remaining = *((int8_t *)addr); break; default: return (dt_set_errno(dtp, EDT_BADNORMAL)); } if (remaining < 0) { func = dtrace_aggregate_walk_valsorted; remaining = -remaining; } else { func = dtrace_aggregate_walk_valrevsorted; } assert(remaining >= 0); trunc.dttd_remaining = remaining; (void) func(dtp, dt_trunc_agg, &trunc); return (0); } static int dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, caddr_t addr, size_t size, const dtrace_aggdata_t *aggdata, uint64_t normal, dt_print_aggdata_t *pd) { int err, width; dtrace_actkind_t act = rec->dtrd_action; boolean_t packed = pd->dtpa_agghist || pd->dtpa_aggpack; dtrace_aggdesc_t *agg = aggdata->dtada_desc; static struct { size_t size; int width; int packedwidth; } *fmt, fmttab[] = { { sizeof (uint8_t), 3, 3 }, { sizeof (uint16_t), 5, 5 }, { sizeof (uint32_t), 8, 8 }, { sizeof (uint64_t), 16, 16 }, { 0, -50, 16 } }; if (packed && pd->dtpa_agghisthdr != agg->dtagd_varid) { dtrace_recdesc_t *r; width = 0; /* * To print our quantization header for either an agghist or * aggpack aggregation, we need to iterate through all of our * of our records to determine their width. */ for (r = rec; !DTRACEACT_ISAGG(r->dtrd_action); r++) { for (fmt = fmttab; fmt->size && fmt->size != r->dtrd_size; fmt++) continue; width += fmt->packedwidth + 1; } if (pd->dtpa_agghist) { if (dt_print_quanthdr(dtp, fp, width) < 0) return (-1); } else { if (dt_print_quanthdr_packed(dtp, fp, width, aggdata, r->dtrd_action) < 0) return (-1); } pd->dtpa_agghisthdr = agg->dtagd_varid; } if (pd->dtpa_agghist && DTRACEACT_ISAGG(act)) { char positives = aggdata->dtada_flags & DTRACE_A_HASPOSITIVES; char negatives = aggdata->dtada_flags & DTRACE_A_HASNEGATIVES; int64_t val; assert(act == DTRACEAGG_SUM || act == DTRACEAGG_COUNT); val = (long long)*((uint64_t *)addr); if (dt_printf(dtp, fp, " ") < 0) return (-1); return (dt_print_quantline(dtp, fp, val, normal, aggdata->dtada_total, positives, negatives)); } if (pd->dtpa_aggpack && DTRACEACT_ISAGG(act)) { switch (act) { case DTRACEAGG_QUANTIZE: return (dt_print_quantize_packed(dtp, fp, addr, size, aggdata)); case DTRACEAGG_LQUANTIZE: return (dt_print_lquantize_packed(dtp, fp, addr, size, aggdata)); default: break; } } switch (act) { case DTRACEACT_STACK: return (dt_print_stack(dtp, fp, NULL, addr, rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); case DTRACEACT_USTACK: case DTRACEACT_JSTACK: return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); case DTRACEACT_USYM: case DTRACEACT_UADDR: return (dt_print_usym(dtp, fp, addr, act)); case DTRACEACT_UMOD: return (dt_print_umod(dtp, fp, NULL, addr)); case DTRACEACT_SYM: return (dt_print_sym(dtp, fp, NULL, addr)); case DTRACEACT_MOD: return (dt_print_mod(dtp, fp, NULL, addr)); case DTRACEAGG_QUANTIZE: return (dt_print_quantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LQUANTIZE: return (dt_print_lquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_LLQUANTIZE: return (dt_print_llquantize(dtp, fp, addr, size, normal)); case DTRACEAGG_AVG: return (dt_print_average(dtp, fp, addr, size, normal)); case DTRACEAGG_STDDEV: return (dt_print_stddev(dtp, fp, addr, size, normal)); default: break; } for (fmt = fmttab; fmt->size && fmt->size != size; fmt++) continue; width = packed ? fmt->packedwidth : fmt->width; switch (size) { case sizeof (uint64_t): err = dt_printf(dtp, fp, " %*lld", width, /* LINTED - alignment */ (long long)*((uint64_t *)addr) / normal); break; case sizeof (uint32_t): /* LINTED - alignment */ err = dt_printf(dtp, fp, " %*d", width, *((uint32_t *)addr) / (uint32_t)normal); break; case sizeof (uint16_t): /* LINTED - alignment */ err = dt_printf(dtp, fp, " %*d", width, *((uint16_t *)addr) / (uint32_t)normal); break; case sizeof (uint8_t): err = dt_printf(dtp, fp, " %*d", width, *((uint8_t *)addr) / (uint32_t)normal); break; default: err = dt_print_bytes(dtp, fp, addr, size, width, 0, 0); break; } return (err); } int dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) { int i, aggact = 0; dt_print_aggdata_t *pd = arg; const dtrace_aggdata_t *aggdata = aggsdata[0]; dtrace_aggdesc_t *agg = aggdata->dtada_desc; FILE *fp = pd->dtpa_fp; dtrace_hdl_t *dtp = pd->dtpa_dtp; dtrace_recdesc_t *rec; dtrace_actkind_t act; caddr_t addr; size_t size; pd->dtpa_agghist = (aggdata->dtada_flags & DTRACE_A_TOTAL); pd->dtpa_aggpack = (aggdata->dtada_flags & DTRACE_A_MINMAXBIN); /* * Iterate over each record description in the key, printing the traced * data, skipping the first datum (the tuple member created by the * compiler). */ for (i = 1; i < agg->dtagd_nrecs; i++) { rec = &agg->dtagd_rec[i]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; if (DTRACEACT_ISAGG(act)) { aggact = i; break; } if (dt_print_datum(dtp, fp, rec, addr, size, aggdata, 1, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGKEY) < 0) return (-1); } assert(aggact != 0); for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { uint64_t normal; aggdata = aggsdata[i]; agg = aggdata->dtada_desc; rec = &agg->dtagd_rec[aggact]; act = rec->dtrd_action; addr = aggdata->dtada_data + rec->dtrd_offset; size = rec->dtrd_size; assert(DTRACEACT_ISAGG(act)); normal = aggdata->dtada_normal; if (dt_print_datum(dtp, fp, rec, addr, size, aggdata, normal, pd) < 0) return (-1); if (dt_buffered_flush(dtp, NULL, rec, aggdata, DTRACE_BUFDATA_AGGVAL) < 0) return (-1); if (!pd->dtpa_allunprint) agg->dtagd_flags |= DTRACE_AGD_PRINTED; } if (!pd->dtpa_agghist && !pd->dtpa_aggpack) { if (dt_printf(dtp, fp, "\n") < 0) return (-1); } if (dt_buffered_flush(dtp, NULL, NULL, aggdata, DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) return (-1); return (0); } int dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) { dt_print_aggdata_t *pd = arg; dtrace_aggdesc_t *agg = aggdata->dtada_desc; dtrace_aggvarid_t aggvarid = pd->dtpa_id; if (pd->dtpa_allunprint) { if (agg->dtagd_flags & DTRACE_AGD_PRINTED) return (0); } else { /* * If we're not printing all unprinted aggregations, then the * aggregation variable ID denotes a specific aggregation * variable that we should print -- skip any other aggregations * that we encounter. */ if (agg->dtagd_nrecs == 0) return (0); if (aggvarid != agg->dtagd_varid) return (0); } return (dt_print_aggs(&aggdata, 1, arg)); } int dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, const char *option, const char *value) { int len, rval; char *msg; const char *errstr; dtrace_setoptdata_t optdata; bzero(&optdata, sizeof (optdata)); (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); if (dtrace_setopt(dtp, option, value) == 0) { (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); optdata.dtsda_probe = data; optdata.dtsda_option = option; optdata.dtsda_handle = dtp; if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) return (rval); return (0); } errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); len = strlen(option) + strlen(value) + strlen(errstr) + 80; msg = alloca(len); (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", option, value, errstr); if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) return (0); return (rval); } static int dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, boolean_t just_one, dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) { dtrace_epid_t id; size_t offs; int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); int rval, i, n; uint64_t tracememsize = 0; dtrace_probedata_t data; uint64_t drops; bzero(&data, sizeof (data)); data.dtpda_handle = dtp; data.dtpda_cpu = cpu; data.dtpda_flow = dtp->dt_flow; data.dtpda_indent = dtp->dt_indent; data.dtpda_prefix = dtp->dt_prefix; for (offs = buf->dtbd_oldest; offs < buf->dtbd_size; ) { dtrace_eprobedesc_t *epd; /* * We're guaranteed to have an ID. */ id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); if (id == DTRACE_EPIDNONE) { /* * This is filler to assure proper alignment of the * next record; we simply ignore it. */ offs += sizeof (id); continue; } if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, &data.dtpda_pdesc)) != 0) return (rval); epd = data.dtpda_edesc; data.dtpda_data = buf->dtbd_data + offs; if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { rval = dt_handle(dtp, &data); if (rval == DTRACE_CONSUME_NEXT) goto nextepid; if (rval == DTRACE_CONSUME_ERROR) return (-1); } if (flow) (void) dt_flowindent(dtp, &data, dtp->dt_last_epid, buf, offs); rval = (*efunc)(&data, arg); if (flow) { if (data.dtpda_flow == DTRACEFLOW_ENTRY) data.dtpda_indent += 2; } if (rval == DTRACE_CONSUME_NEXT) goto nextepid; if (rval == DTRACE_CONSUME_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); if (rval != DTRACE_CONSUME_THIS) return (dt_set_errno(dtp, EDT_BADRVAL)); for (i = 0; i < epd->dtepd_nrecs; i++) { caddr_t addr; dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; dtrace_actkind_t act = rec->dtrd_action; data.dtpda_data = buf->dtbd_data + offs + rec->dtrd_offset; addr = data.dtpda_data; if (act == DTRACEACT_LIBACT) { uint64_t arg = rec->dtrd_arg; dtrace_aggvarid_t id; switch (arg) { case DT_ACT_CLEAR: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_clear_agg, &id); continue; case DT_ACT_DENORMALIZE: /* LINTED - alignment */ id = *((dtrace_aggvarid_t *)addr); (void) dtrace_aggregate_walk(dtp, dt_denormalize_agg, &id); continue; case DT_ACT_FTRUNCATE: if (fp == NULL) continue; (void) fflush(fp); (void) ftruncate(fileno(fp), 0); (void) fseeko(fp, 0, SEEK_SET); continue; case DT_ACT_NORMALIZE: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADNORMAL)); if (dt_normalize(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; case DT_ACT_SETOPT: { uint64_t *opts = dtp->dt_options; dtrace_recdesc_t *valrec; uint32_t valsize; caddr_t val; int rv; if (i == epd->dtepd_nrecs - 1) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } valrec = &epd->dtepd_rec[++i]; valsize = valrec->dtrd_size; if (valrec->dtrd_action != act || valrec->dtrd_arg != arg) { return (dt_set_errno(dtp, EDT_BADSETOPT)); } if (valsize > sizeof (uint64_t)) { val = buf->dtbd_data + offs + valrec->dtrd_offset; } else { val = "1"; } rv = dt_setopt(dtp, &data, addr, val); if (rv != 0) return (-1); flow = (opts[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); quiet = (opts[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); continue; } case DT_ACT_TRUNC: if (i == epd->dtepd_nrecs - 1) return (dt_set_errno(dtp, EDT_BADTRUNC)); if (dt_trunc(dtp, buf->dtbd_data + offs, rec) != 0) return (-1); i++; continue; default: continue; } } if (act == DTRACEACT_TRACEMEM_DYNSIZE && rec->dtrd_size == sizeof (uint64_t)) { /* LINTED - alignment */ tracememsize = *((unsigned long long *)addr); continue; } rval = (*rfunc)(&data, rec, arg); if (rval == DTRACE_CONSUME_NEXT) continue; if (rval == DTRACE_CONSUME_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); if (rval != DTRACE_CONSUME_THIS) return (dt_set_errno(dtp, EDT_BADRVAL)); if (act == DTRACEACT_STACK) { int depth = rec->dtrd_arg; if (dt_print_stack(dtp, fp, NULL, addr, depth, rec->dtrd_size / depth) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_USTACK || act == DTRACEACT_JSTACK) { if (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_SYM) { if (dt_print_sym(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_MOD) { if (dt_print_mod(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { if (dt_print_usym(dtp, fp, addr, act) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_UMOD) { if (dt_print_umod(dtp, fp, NULL, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_PRINTM) { if (dt_print_memory(dtp, fp, addr) < 0) return (-1); goto nextrec; } if (act == DTRACEACT_PRINTT) { if (dt_print_type(dtp, fp, addr) < 0) return (-1); goto nextrec; } if (DTRACEACT_ISPRINTFLIKE(act)) { void *fmtdata; int (*func)(dtrace_hdl_t *, FILE *, void *, const dtrace_probedata_t *, const dtrace_recdesc_t *, uint_t, const void *buf, size_t); if ((fmtdata = dt_format_lookup(dtp, rec->dtrd_format)) == NULL) goto nofmt; switch (act) { case DTRACEACT_PRINTF: func = dtrace_fprintf; break; case DTRACEACT_PRINTA: func = dtrace_fprinta; break; case DTRACEACT_SYSTEM: func = dtrace_system; break; case DTRACEACT_FREOPEN: func = dtrace_freopen; break; } n = (*func)(dtp, fp, fmtdata, &data, rec, epd->dtepd_nrecs - i, (uchar_t *)buf->dtbd_data + offs, buf->dtbd_size - offs); if (n < 0) return (-1); /* errno is set for us */ if (n > 0) i += n - 1; goto nextrec; } /* * If this is a DIF expression, and the record has a * format set, this indicates we have a CTF type name * associated with the data and we should try to print * it out by type. */ if (act == DTRACEACT_DIFEXPR) { const char *strdata = dt_strdata_lookup(dtp, rec->dtrd_format); if (strdata != NULL) { n = dtrace_print(dtp, fp, strdata, addr, rec->dtrd_size); /* * dtrace_print() will return -1 on * error, or return the number of bytes * consumed. It will return 0 if the * type couldn't be determined, and we * should fall through to the normal * trace method. */ if (n < 0) return (-1); if (n > 0) goto nextrec; } } nofmt: if (act == DTRACEACT_PRINTA) { dt_print_aggdata_t pd; dtrace_aggvarid_t *aggvars; int j, naggvars = 0; size_t size = ((epd->dtepd_nrecs - i) * sizeof (dtrace_aggvarid_t)); if ((aggvars = dt_alloc(dtp, size)) == NULL) return (-1); /* * This might be a printa() with multiple * aggregation variables. We need to scan * forward through the records until we find * a record from a different statement. */ for (j = i; j < epd->dtepd_nrecs; j++) { dtrace_recdesc_t *nrec; caddr_t naddr; nrec = &epd->dtepd_rec[j]; if (nrec->dtrd_uarg != rec->dtrd_uarg) break; if (nrec->dtrd_action != act) { return (dt_set_errno(dtp, EDT_BADAGG)); } naddr = buf->dtbd_data + offs + nrec->dtrd_offset; aggvars[naggvars++] = /* LINTED - alignment */ *((dtrace_aggvarid_t *)naddr); } i = j - 1; bzero(&pd, sizeof (pd)); pd.dtpa_dtp = dtp; pd.dtpa_fp = fp; assert(naggvars >= 1); if (naggvars == 1) { pd.dtpa_id = aggvars[0]; dt_free(dtp, aggvars); if (dt_printf(dtp, fp, "\n") < 0 || dtrace_aggregate_walk_sorted(dtp, dt_print_agg, &pd) < 0) return (-1); goto nextrec; } if (dt_printf(dtp, fp, "\n") < 0 || dtrace_aggregate_walk_joined(dtp, aggvars, naggvars, dt_print_aggs, &pd) < 0) { dt_free(dtp, aggvars); return (-1); } dt_free(dtp, aggvars); goto nextrec; } if (act == DTRACEACT_TRACEMEM) { if (tracememsize == 0 || tracememsize > rec->dtrd_size) { tracememsize = rec->dtrd_size; } n = dt_print_bytes(dtp, fp, addr, tracememsize, -33, quiet, 1); tracememsize = 0; if (n < 0) return (-1); goto nextrec; } switch (rec->dtrd_size) { case sizeof (uint64_t): n = dt_printf(dtp, fp, quiet ? "%lld" : " %16lld", /* LINTED - alignment */ *((unsigned long long *)addr)); break; case sizeof (uint32_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", /* LINTED - alignment */ *((uint32_t *)addr)); break; case sizeof (uint16_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", /* LINTED - alignment */ *((uint16_t *)addr)); break; case sizeof (uint8_t): n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", *((uint8_t *)addr)); break; default: n = dt_print_bytes(dtp, fp, addr, rec->dtrd_size, -33, quiet, 0); break; } if (n < 0) return (-1); /* errno is set for us */ nextrec: if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) return (-1); /* errno is set for us */ } /* * Call the record callback with a NULL record to indicate * that we're done processing this EPID. */ rval = (*rfunc)(&data, NULL, arg); nextepid: offs += epd->dtepd_size; dtp->dt_last_epid = id; if (just_one) { buf->dtbd_oldest = offs; break; } } dtp->dt_flow = data.dtpda_flow; dtp->dt_indent = data.dtpda_indent; dtp->dt_prefix = data.dtpda_prefix; if ((drops = buf->dtbd_drops) == 0) return (0); /* * Explicitly zero the drops to prevent us from processing them again. */ buf->dtbd_drops = 0; return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); } /* * Reduce memory usage by shrinking the buffer if it's no more than half full. * Note, we need to preserve the alignment of the data at dtbd_oldest, which is * only 4-byte aligned. */ static void dt_realloc_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf, int cursize) { uint64_t used = buf->dtbd_size - buf->dtbd_oldest; if (used < cursize / 2) { int misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); char *newdata = dt_alloc(dtp, used + misalign); if (newdata == NULL) return; bzero(newdata, misalign); bcopy(buf->dtbd_data + buf->dtbd_oldest, newdata + misalign, used); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = misalign; buf->dtbd_size = used + misalign; buf->dtbd_data = newdata; } } /* * If the ring buffer has wrapped, the data is not in order. Rearrange it * so that it is. Note, we need to preserve the alignment of the data at * dtbd_oldest, which is only 4-byte aligned. */ static int dt_unring_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { int misalign; char *newdata, *ndp; if (buf->dtbd_oldest == 0) return (0); misalign = buf->dtbd_oldest & (sizeof (uint64_t) - 1); newdata = ndp = dt_alloc(dtp, buf->dtbd_size + misalign); if (newdata == NULL) return (-1); assert(0 == (buf->dtbd_size & (sizeof (uint64_t) - 1))); bzero(ndp, misalign); ndp += misalign; bcopy(buf->dtbd_data + buf->dtbd_oldest, ndp, buf->dtbd_size - buf->dtbd_oldest); ndp += buf->dtbd_size - buf->dtbd_oldest; bcopy(buf->dtbd_data, ndp, buf->dtbd_oldest); dt_free(dtp, buf->dtbd_data); buf->dtbd_oldest = 0; buf->dtbd_data = newdata; buf->dtbd_size += misalign; return (0); } static void dt_put_buf(dtrace_hdl_t *dtp, dtrace_bufdesc_t *buf) { dt_free(dtp, buf->dtbd_data); dt_free(dtp, buf); } /* * Returns 0 on success, in which case *cbp will be filled in if we retrieved * data, or NULL if there is no data for this CPU. * Returns -1 on failure and sets dt_errno. */ static int dt_get_buf(dtrace_hdl_t *dtp, int cpu, dtrace_bufdesc_t **bufp) { dtrace_optval_t size; dtrace_bufdesc_t *buf = dt_zalloc(dtp, sizeof (*buf)); int error, rval; if (buf == NULL) return (-1); (void) dtrace_getopt(dtp, "bufsize", &size); buf->dtbd_data = dt_alloc(dtp, size); if (buf->dtbd_data == NULL) { dt_free(dtp, buf); return (-1); } buf->dtbd_size = size; buf->dtbd_cpu = cpu; #ifdef illumos if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { #else if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { #endif /* * If we failed with ENOENT, it may be because the * CPU was unconfigured -- this is okay. Any other * error, however, is unexpected. */ if (errno == ENOENT) { *bufp = NULL; rval = 0; } else rval = dt_set_errno(dtp, errno); dt_put_buf(dtp, buf); return (rval); } error = dt_unring_buf(dtp, buf); if (error != 0) { dt_put_buf(dtp, buf); return (error); } dt_realloc_buf(dtp, buf, size); *bufp = buf; return (0); } typedef struct dt_begin { dtrace_consume_probe_f *dtbgn_probefunc; dtrace_consume_rec_f *dtbgn_recfunc; void *dtbgn_arg; dtrace_handle_err_f *dtbgn_errhdlr; void *dtbgn_errarg; int dtbgn_beginonly; } dt_begin_t; static int dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) { dt_begin_t *begin = arg; dtrace_probedesc_t *pd = data->dtpda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_CONSUME_NEXT); } else { if (r1 && r2) return (DTRACE_CONSUME_NEXT); } /* * We have a record that we're interested in. Now call the underlying * probe function... */ return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); } static int dt_consume_begin_record(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) { dt_begin_t *begin = arg; return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); } static int dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) { dt_begin_t *begin = (dt_begin_t *)arg; dtrace_probedesc_t *pd = data->dteda_pdesc; int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); if (begin->dtbgn_beginonly) { if (!(r1 && r2)) return (DTRACE_HANDLE_OK); } else { if (r1 && r2) return (DTRACE_HANDLE_OK); } return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); } static int dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { /* * There's this idea that the BEGIN probe should be processed before * everything else, and that the END probe should be processed after * anything else. In the common case, this is pretty easy to deal * with. However, a situation may arise where the BEGIN enabling and * END enabling are on the same CPU, and some enabling in the middle * occurred on a different CPU. To deal with this (blech!) we need to * consume the BEGIN buffer up until the end of the BEGIN probe, and * then set it aside. We will then process every other CPU, and then * we'll return to the BEGIN CPU and process the rest of the data * (which will inevitably include the END probe, if any). Making this * even more complicated (!) is the library's ERROR enabling. Because * this enabling is processed before we even get into the consume call * back, any ERROR firing would result in the library's ERROR enabling * being processed twice -- once in our first pass (for BEGIN probes), * and again in our second pass (for everything but BEGIN probes). To * deal with this, we interpose on the ERROR handler to assure that we * only process ERROR enablings induced by BEGIN enablings in the * first pass, and that we only process ERROR enablings _not_ induced * by BEGIN enablings in the second pass. */ dt_begin_t begin; processorid_t cpu = dtp->dt_beganon; int rval, i; static int max_ncpus; dtrace_bufdesc_t *buf; dtp->dt_beganon = -1; if (dt_get_buf(dtp, cpu, &buf) != 0) return (-1); if (buf == NULL) return (0); if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { /* * This is the simple case. We're either not stopped, or if * we are, we actually processed any END probes on another * CPU. We can simply consume this buffer and return. */ rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } begin.dtbgn_probefunc = pf; begin.dtbgn_recfunc = rf; begin.dtbgn_arg = arg; begin.dtbgn_beginonly = 1; /* * We need to interpose on the ERROR handler to be sure that we * only process ERRORs induced by BEGIN. */ begin.dtbgn_errhdlr = dtp->dt_errhdlr; begin.dtbgn_errarg = dtp->dt_errarg; dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *nbuf; if (i == cpu) continue; if (dt_get_buf(dtp, i, &nbuf) != 0) { dt_put_buf(dtp, buf); return (-1); } if (nbuf == NULL) continue; rval = dt_consume_cpu(dtp, fp, i, nbuf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, nbuf); if (rval != 0) { dt_put_buf(dtp, buf); return (rval); } } /* * Okay -- we're done with the other buffers. Now we want to * reconsume the first buffer -- but this time we're looking for * everything _but_ BEGIN. And of course, in order to only consume * those ERRORs _not_ associated with BEGIN, we need to reinstall our * ERROR interposition function... */ begin.dtbgn_beginonly = 0; assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); assert(begin.dtbgn_errarg == dtp->dt_errarg); dtp->dt_errhdlr = dt_consume_begin_error; dtp->dt_errarg = &begin; rval = dt_consume_cpu(dtp, fp, cpu, buf, B_FALSE, dt_consume_begin_probe, dt_consume_begin_record, &begin); dtp->dt_errhdlr = begin.dtbgn_errhdlr; dtp->dt_errarg = begin.dtbgn_errarg; return (rval); } /* ARGSUSED */ static uint64_t dt_buf_oldest(void *elem, void *arg) { dtrace_bufdesc_t *buf = elem; size_t offs = buf->dtbd_oldest; while (offs < buf->dtbd_size) { dtrace_rechdr_t *dtrh = /* LINTED - alignment */ (dtrace_rechdr_t *)(buf->dtbd_data + offs); if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { offs += sizeof (dtrace_epid_t); } else { return (DTRACE_RECORD_LOAD_TIMESTAMP(dtrh)); } } /* There are no records left; use the time the buffer was retrieved. */ return (buf->dtbd_timestamp); } int dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) { dtrace_optval_t size; static int max_ncpus; int i, rval; dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; hrtime_t now = gethrtime(); if (dtp->dt_lastswitch != 0) { if (now - dtp->dt_lastswitch < interval) return (0); dtp->dt_lastswitch += interval; } else { dtp->dt_lastswitch = now; } if (!dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); if (max_ncpus == 0) max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; if (pf == NULL) pf = (dtrace_consume_probe_f *)dt_nullprobe; if (rf == NULL) rf = (dtrace_consume_rec_f *)dt_nullrec; if (dtp->dt_options[DTRACEOPT_TEMPORAL] == DTRACEOPT_UNSET) { /* * The output will not be in the order it was traced. Rather, * we will consume all of the data from each CPU's buffer in * turn. We apply special handling for the records from BEGIN * and END probes so that they are consumed first and last, * respectively. * * If we have just begun, we want to first process the CPU that * executed the BEGIN probe (if any). */ if (dtp->dt_active && dtp->dt_beganon != -1 && (rval = dt_consume_begin(dtp, fp, pf, rf, arg)) != 0) return (rval); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; /* * If we have stopped, we want to process the CPU on * which the END probe was processed only _after_ we * have processed everything else. */ if (dtp->dt_stopped && (i == dtp->dt_endedon)) continue; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf == NULL) continue; dtp->dt_flow = 0; dtp->dt_indent = 0; dtp->dt_prefix = NULL; rval = dt_consume_cpu(dtp, fp, i, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); if (rval != 0) return (rval); } if (dtp->dt_stopped) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, dtp->dt_endedon, &buf) != 0) return (-1); if (buf == NULL) return (0); rval = dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, B_FALSE, pf, rf, arg); dt_put_buf(dtp, buf); return (rval); } } else { /* * The output will be in the order it was traced (or for * speculations, when it was committed). We retrieve a buffer * from each CPU and put it into a priority queue, which sorts * based on the first entry in the buffer. This is sufficient * because entries within a buffer are already sorted. * * We then consume records one at a time, always consuming the * oldest record, as determined by the priority queue. When * we reach the end of the time covered by these buffers, * we need to stop and retrieve more records on the next pass. * The kernel tells us the time covered by each buffer, in * dtbd_timestamp. The first buffer's timestamp tells us the * time covered by all buffers, as subsequently retrieved * buffers will cover to a more recent time. */ uint64_t *drops = alloca(max_ncpus * sizeof (uint64_t)); uint64_t first_timestamp = 0; uint_t cookie = 0; dtrace_bufdesc_t *buf; bzero(drops, max_ncpus * sizeof (uint64_t)); if (dtp->dt_bufq == NULL) { dtp->dt_bufq = dt_pq_init(dtp, max_ncpus * 2, dt_buf_oldest, NULL); if (dtp->dt_bufq == NULL) /* ENOMEM */ return (-1); } /* Retrieve data from each CPU. */ (void) dtrace_getopt(dtp, "bufsize", &size); for (i = 0; i < max_ncpus; i++) { dtrace_bufdesc_t *buf; if (dt_get_buf(dtp, i, &buf) != 0) return (-1); if (buf != NULL) { if (first_timestamp == 0) first_timestamp = buf->dtbd_timestamp; assert(buf->dtbd_timestamp >= first_timestamp); dt_pq_insert(dtp->dt_bufq, buf); drops[i] = buf->dtbd_drops; buf->dtbd_drops = 0; } } /* Consume records. */ for (;;) { dtrace_bufdesc_t *buf = dt_pq_pop(dtp->dt_bufq); uint64_t timestamp; if (buf == NULL) break; timestamp = dt_buf_oldest(buf, dtp); assert(timestamp >= dtp->dt_last_timestamp); dtp->dt_last_timestamp = timestamp; if (timestamp == buf->dtbd_timestamp) { /* * We've reached the end of the time covered * by this buffer. If this is the oldest * buffer, we must do another pass * to retrieve more data. */ dt_put_buf(dtp, buf); if (timestamp == first_timestamp && !dtp->dt_stopped) break; continue; } if ((rval = dt_consume_cpu(dtp, fp, buf->dtbd_cpu, buf, B_TRUE, pf, rf, arg)) != 0) return (rval); dt_pq_insert(dtp->dt_bufq, buf); } /* Consume drops. */ for (i = 0; i < max_ncpus; i++) { if (drops[i] != 0) { int error = dt_handle_cpudrop(dtp, i, DTRACEDROP_PRINCIPAL, drops[i]); if (error != 0) return (error); } } /* * Reduce memory usage by re-allocating smaller buffers * for the "remnants". */ while (buf = dt_pq_walk(dtp->dt_bufq, &cookie)) dt_realloc_buf(dtp, buf, buf->dtbd_size); } return (0); } Index: projects/building-blocks/cddl/contrib/opensolaris =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris (revision 278195) +++ projects/building-blocks/cddl/contrib/opensolaris (revision 278196) Property changes on: projects/building-blocks/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl/contrib/opensolaris:r278050-278194 Index: projects/building-blocks/cddl =================================================================== --- projects/building-blocks/cddl (revision 278195) +++ projects/building-blocks/cddl (revision 278196) Property changes on: projects/building-blocks/cddl ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl:r278050-278194 Index: projects/building-blocks/contrib/llvm/patches/patch-29-llvm-r226664-aarch64-x18.diff =================================================================== --- projects/building-blocks/contrib/llvm/patches/patch-29-llvm-r226664-aarch64-x18.diff (revision 278195) +++ projects/building-blocks/contrib/llvm/patches/patch-29-llvm-r226664-aarch64-x18.diff (nonexistent) @@ -1,83 +0,0 @@ -Pull in r226664 from upstream llvm trunk (by Tim Northover): - - AArch64: add backend option to reserve x18 (platform register) - - AAPCS64 says that it's up to the platform to specify whether x18 is - reserved, and a first step on that way is to add a flag controlling - it. - - From: Andrew Turner - -Introduced here: http://svnweb.freebsd.org/changeset/base/277774 - -Index: lib/Target/AArch64/AArch64RegisterInfo.cpp -=================================================================== ---- lib/Target/AArch64/AArch64RegisterInfo.cpp -+++ lib/Target/AArch64/AArch64RegisterInfo.cpp -@@ -33,6 +33,10 @@ using namespace llvm; - #define GET_REGINFO_TARGET_DESC - #include "AArch64GenRegisterInfo.inc" - -+static cl::opt -+ReserveX18("aarch64-reserve-x18", cl::Hidden, -+ cl::desc("Reserve X18, making it unavailable as GPR")); -+ - AArch64RegisterInfo::AArch64RegisterInfo(const AArch64InstrInfo *tii, - const AArch64Subtarget *sti) - : AArch64GenRegisterInfo(AArch64::LR), TII(tii), STI(sti) {} -@@ -90,7 +94,7 @@ AArch64RegisterInfo::getReservedRegs(const Machine - Reserved.set(AArch64::W29); - } - -- if (STI->isTargetDarwin()) { -+ if (STI->isTargetDarwin() || ReserveX18) { - Reserved.set(AArch64::X18); // Platform register - Reserved.set(AArch64::W18); - } -@@ -117,7 +121,7 @@ bool AArch64RegisterInfo::isReservedReg(const Mach - return true; - case AArch64::X18: - case AArch64::W18: -- return STI->isTargetDarwin(); -+ return STI->isTargetDarwin() || ReserveX18; - case AArch64::FP: - case AArch64::W29: - return TFI->hasFP(MF) || STI->isTargetDarwin(); -@@ -379,7 +383,7 @@ unsigned AArch64RegisterInfo::getRegPressureLimit( - case AArch64::GPR64commonRegClassID: - return 32 - 1 // XZR/SP - - (TFI->hasFP(MF) || STI->isTargetDarwin()) // FP -- - STI->isTargetDarwin() // X18 reserved as platform register -+ - (STI->isTargetDarwin() || ReserveX18) // X18 reserved as platform register - - hasBasePointer(MF); // X19 - case AArch64::FPR8RegClassID: - case AArch64::FPR16RegClassID: -Index: test/CodeGen/AArch64/arm64-platform-reg.ll -=================================================================== ---- test/CodeGen/AArch64/arm64-platform-reg.ll -+++ test/CodeGen/AArch64/arm64-platform-reg.ll -@@ -1,4 +1,5 @@ --; RUN: llc -mtriple=arm64-apple-ios -o - %s | FileCheck %s --check-prefix=CHECK-DARWIN -+; RUN: llc -mtriple=arm64-apple-ios -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE-X18 -+; RUN: llc -mtriple=arm64-freebsd-gnu -aarch64-reserve-x18 -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE-X18 - ; RUN: llc -mtriple=arm64-linux-gnu -o - %s | FileCheck %s - - ; x18 is reserved as a platform register on Darwin but not on other -@@ -16,11 +17,11 @@ define void @keep_live() { - ; CHECK: ldr x18 - ; CHECK: str x18 - --; CHECK-DARWIN-NOT: ldr fp --; CHECK-DARWIN-NOT: ldr x18 --; CHECK-DARWIN: Spill --; CHECK-DARWIN-NOT: ldr fp --; CHECK-DARWIN-NOT: ldr x18 --; CHECK-DARWIN: ret -+; CHECK-RESERVE-X18-NOT: ldr fp -+; CHECK-RESERVE-X18-NOT: ldr x18 -+; CHECK-RESERVE-X18: Spill -+; CHECK-RESERVE-X18-NOT: ldr fp -+; CHECK-RESERVE-X18-NOT: ldr x18 -+; CHECK-RESERVE-X18: ret - ret void - } Index: projects/building-blocks/contrib/llvm/patches/patch-30-clang-r227062-fixes-x18.diff =================================================================== --- projects/building-blocks/contrib/llvm/patches/patch-30-clang-r227062-fixes-x18.diff (revision 278195) +++ projects/building-blocks/contrib/llvm/patches/patch-30-clang-r227062-fixes-x18.diff (nonexistent) @@ -1,54 +0,0 @@ -Pull in r227062 from upstream clang trunk (by Renato Golin): - - Allows Clang to use LLVM's fixes-x18 option - - This patch allows clang to have llvm reserve the x18 - platform register on AArch64. FreeBSD will use this in the kernel for - per-cpu data but has no need to reserve this register in userland so - will need this flag to reserve it. - - This uses llvm r226664 to allow this register to be reserved. - - Patch by Andrew Turner. - -Introduced here: http://svnweb.freebsd.org/changeset/base/277775 - -Index: tools/clang/include/clang/Driver/Options.td -=================================================================== ---- tools/clang/include/clang/Driver/Options.td -+++ tools/clang/include/clang/Driver/Options.td -@@ -1141,6 +1141,9 @@ def mno_long_calls : Flag<["-"], "mno-long-calls"> - def mgeneral_regs_only : Flag<["-"], "mgeneral-regs-only">, Group, - HelpText<"Generate code which only uses the general purpose registers (AArch64 only)">; - -+def ffixed_x18 : Flag<["-"], "ffixed-x18">, Group, -+ HelpText<"Reserve the x18 register (AArch64 only)">; -+ - def mvsx : Flag<["-"], "mvsx">, Group; - def mno_vsx : Flag<["-"], "mno-vsx">, Group; - def mfprnd : Flag<["-"], "mfprnd">, Group; -Index: tools/clang/lib/Driver/Tools.cpp -=================================================================== ---- tools/clang/lib/Driver/Tools.cpp -+++ tools/clang/lib/Driver/Tools.cpp -@@ -887,6 +887,11 @@ void Clang::AddAArch64TargetArgs(const ArgList &Ar - if (A->getOption().matches(options::OPT_mno_global_merge)) - CmdArgs.push_back("-mno-global-merge"); - } -+ -+ if (Args.hasArg(options::OPT_ffixed_x18)) { -+ CmdArgs.push_back("-backend-option"); -+ CmdArgs.push_back("-aarch64-reserve-x18"); -+ } - } - - // Get CPU and ABI names. They are not independent -Index: tools/clang/test/Driver/aarch64-fixed-x18.c -=================================================================== ---- tools/clang/test/Driver/aarch64-fixed-x18.c -+++ tools/clang/test/Driver/aarch64-fixed-x18.c -@@ -0,0 +1,4 @@ -+// RUN: %clang -target aarch64-none-gnu -ffixed-x18 -### %s 2> %t -+// RUN: FileCheck --check-prefix=CHECK-FIXED-X18 < %t %s -+ -+// CHECK-FIXED-X18: "-backend-option" "-aarch64-reserve-x18" Index: projects/building-blocks/contrib/llvm/patches/patch-29-clang-add-mips-triples.diff =================================================================== --- projects/building-blocks/contrib/llvm/patches/patch-29-clang-add-mips-triples.diff (nonexistent) +++ projects/building-blocks/contrib/llvm/patches/patch-29-clang-add-mips-triples.diff (revision 278196) @@ -0,0 +1,33 @@ +Allow clang to be built for mips/mips64 backend types by adding our mips +triple ids + +This only allows testing and does not change the defaults for mips/mips64. +They still build/use gcc by default. + +Differential Revision: https://reviews.freebsd.org/D1190 +Reviewed by: dim + +Introduced here: http://svnweb.freebsd.org/changeset/base/277423 + +Index: tools/clang/lib/Driver/Tools.cpp +=================================================================== +--- tools/clang/lib/Driver/Tools.cpp ++++ tools/clang/lib/Driver/Tools.cpp +@@ -6592,6 +6592,17 @@ void freebsd::Link::ConstructJob(Compilation &C, c + CmdArgs.push_back("elf32ppc_fbsd"); + } + ++ if (Arg *A = Args.getLastArg(options::OPT_G)) { ++ if (ToolChain.getArch() == llvm::Triple::mips || ++ ToolChain.getArch() == llvm::Triple::mipsel || ++ ToolChain.getArch() == llvm::Triple::mips64 || ++ ToolChain.getArch() == llvm::Triple::mips64el) { ++ StringRef v = A->getValue(); ++ CmdArgs.push_back(Args.MakeArgString("-G" + v)); ++ A->claim(); ++ } ++ } ++ + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); Index: projects/building-blocks/contrib/llvm/patches/patch-30-llvm-r226664-aarch64-x18.diff =================================================================== --- projects/building-blocks/contrib/llvm/patches/patch-30-llvm-r226664-aarch64-x18.diff (nonexistent) +++ projects/building-blocks/contrib/llvm/patches/patch-30-llvm-r226664-aarch64-x18.diff (revision 278196) @@ -0,0 +1,83 @@ +Pull in r226664 from upstream llvm trunk (by Tim Northover): + + AArch64: add backend option to reserve x18 (platform register) + + AAPCS64 says that it's up to the platform to specify whether x18 is + reserved, and a first step on that way is to add a flag controlling + it. + + From: Andrew Turner + +Introduced here: http://svnweb.freebsd.org/changeset/base/277774 + +Index: lib/Target/AArch64/AArch64RegisterInfo.cpp +=================================================================== +--- lib/Target/AArch64/AArch64RegisterInfo.cpp ++++ lib/Target/AArch64/AArch64RegisterInfo.cpp +@@ -33,6 +33,10 @@ using namespace llvm; + #define GET_REGINFO_TARGET_DESC + #include "AArch64GenRegisterInfo.inc" + ++static cl::opt ++ReserveX18("aarch64-reserve-x18", cl::Hidden, ++ cl::desc("Reserve X18, making it unavailable as GPR")); ++ + AArch64RegisterInfo::AArch64RegisterInfo(const AArch64InstrInfo *tii, + const AArch64Subtarget *sti) + : AArch64GenRegisterInfo(AArch64::LR), TII(tii), STI(sti) {} +@@ -90,7 +94,7 @@ AArch64RegisterInfo::getReservedRegs(const Machine + Reserved.set(AArch64::W29); + } + +- if (STI->isTargetDarwin()) { ++ if (STI->isTargetDarwin() || ReserveX18) { + Reserved.set(AArch64::X18); // Platform register + Reserved.set(AArch64::W18); + } +@@ -117,7 +121,7 @@ bool AArch64RegisterInfo::isReservedReg(const Mach + return true; + case AArch64::X18: + case AArch64::W18: +- return STI->isTargetDarwin(); ++ return STI->isTargetDarwin() || ReserveX18; + case AArch64::FP: + case AArch64::W29: + return TFI->hasFP(MF) || STI->isTargetDarwin(); +@@ -379,7 +383,7 @@ unsigned AArch64RegisterInfo::getRegPressureLimit( + case AArch64::GPR64commonRegClassID: + return 32 - 1 // XZR/SP + - (TFI->hasFP(MF) || STI->isTargetDarwin()) // FP +- - STI->isTargetDarwin() // X18 reserved as platform register ++ - (STI->isTargetDarwin() || ReserveX18) // X18 reserved as platform register + - hasBasePointer(MF); // X19 + case AArch64::FPR8RegClassID: + case AArch64::FPR16RegClassID: +Index: test/CodeGen/AArch64/arm64-platform-reg.ll +=================================================================== +--- test/CodeGen/AArch64/arm64-platform-reg.ll ++++ test/CodeGen/AArch64/arm64-platform-reg.ll +@@ -1,4 +1,5 @@ +-; RUN: llc -mtriple=arm64-apple-ios -o - %s | FileCheck %s --check-prefix=CHECK-DARWIN ++; RUN: llc -mtriple=arm64-apple-ios -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE-X18 ++; RUN: llc -mtriple=arm64-freebsd-gnu -aarch64-reserve-x18 -o - %s | FileCheck %s --check-prefix=CHECK-RESERVE-X18 + ; RUN: llc -mtriple=arm64-linux-gnu -o - %s | FileCheck %s + + ; x18 is reserved as a platform register on Darwin but not on other +@@ -16,11 +17,11 @@ define void @keep_live() { + ; CHECK: ldr x18 + ; CHECK: str x18 + +-; CHECK-DARWIN-NOT: ldr fp +-; CHECK-DARWIN-NOT: ldr x18 +-; CHECK-DARWIN: Spill +-; CHECK-DARWIN-NOT: ldr fp +-; CHECK-DARWIN-NOT: ldr x18 +-; CHECK-DARWIN: ret ++; CHECK-RESERVE-X18-NOT: ldr fp ++; CHECK-RESERVE-X18-NOT: ldr x18 ++; CHECK-RESERVE-X18: Spill ++; CHECK-RESERVE-X18-NOT: ldr fp ++; CHECK-RESERVE-X18-NOT: ldr x18 ++; CHECK-RESERVE-X18: ret + ret void + } Index: projects/building-blocks/contrib/llvm/patches/patch-31-clang-r227062-fixes-x18.diff =================================================================== --- projects/building-blocks/contrib/llvm/patches/patch-31-clang-r227062-fixes-x18.diff (nonexistent) +++ projects/building-blocks/contrib/llvm/patches/patch-31-clang-r227062-fixes-x18.diff (revision 278196) @@ -0,0 +1,54 @@ +Pull in r227062 from upstream clang trunk (by Renato Golin): + + Allows Clang to use LLVM's fixes-x18 option + + This patch allows clang to have llvm reserve the x18 + platform register on AArch64. FreeBSD will use this in the kernel for + per-cpu data but has no need to reserve this register in userland so + will need this flag to reserve it. + + This uses llvm r226664 to allow this register to be reserved. + + Patch by Andrew Turner. + +Introduced here: http://svnweb.freebsd.org/changeset/base/277775 + +Index: tools/clang/include/clang/Driver/Options.td +=================================================================== +--- tools/clang/include/clang/Driver/Options.td ++++ tools/clang/include/clang/Driver/Options.td +@@ -1141,6 +1141,9 @@ def mno_long_calls : Flag<["-"], "mno-long-calls"> + def mgeneral_regs_only : Flag<["-"], "mgeneral-regs-only">, Group, + HelpText<"Generate code which only uses the general purpose registers (AArch64 only)">; + ++def ffixed_x18 : Flag<["-"], "ffixed-x18">, Group, ++ HelpText<"Reserve the x18 register (AArch64 only)">; ++ + def mvsx : Flag<["-"], "mvsx">, Group; + def mno_vsx : Flag<["-"], "mno-vsx">, Group; + def mfprnd : Flag<["-"], "mfprnd">, Group; +Index: tools/clang/lib/Driver/Tools.cpp +=================================================================== +--- tools/clang/lib/Driver/Tools.cpp ++++ tools/clang/lib/Driver/Tools.cpp +@@ -887,6 +887,11 @@ void Clang::AddAArch64TargetArgs(const ArgList &Ar + if (A->getOption().matches(options::OPT_mno_global_merge)) + CmdArgs.push_back("-mno-global-merge"); + } ++ ++ if (Args.hasArg(options::OPT_ffixed_x18)) { ++ CmdArgs.push_back("-backend-option"); ++ CmdArgs.push_back("-aarch64-reserve-x18"); ++ } + } + + // Get CPU and ABI names. They are not independent +Index: tools/clang/test/Driver/aarch64-fixed-x18.c +=================================================================== +--- tools/clang/test/Driver/aarch64-fixed-x18.c ++++ tools/clang/test/Driver/aarch64-fixed-x18.c +@@ -0,0 +1,4 @@ ++// RUN: %clang -target aarch64-none-gnu -ffixed-x18 -### %s 2> %t ++// RUN: FileCheck --check-prefix=CHECK-FIXED-X18 < %t %s ++ ++// CHECK-FIXED-X18: "-backend-option" "-aarch64-reserve-x18" Index: projects/building-blocks/contrib/llvm =================================================================== --- projects/building-blocks/contrib/llvm (revision 278195) +++ projects/building-blocks/contrib/llvm (revision 278196) Property changes on: projects/building-blocks/contrib/llvm ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/llvm:r277784-278194 Index: projects/building-blocks/etc =================================================================== --- projects/building-blocks/etc (revision 278195) +++ projects/building-blocks/etc (revision 278196) Property changes on: projects/building-blocks/etc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/etc:r277872-278194 Index: projects/building-blocks/lib/msun/src/s_scalbln.c =================================================================== --- projects/building-blocks/lib/msun/src/s_scalbln.c (revision 278195) +++ projects/building-blocks/lib/msun/src/s_scalbln.c (revision 278196) @@ -1,58 +1,64 @@ /*- * Copyright (c) 2004 David Schultz * 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 #include double scalbln (double x, long n) { int in; - in = (n > INT_MAX) ? INT_MAX : (n < INT_MIN) ? INT_MIN : n; + in = (int)n; + if (in != n) + in = (n > 0) ? INT_MAX: INT_MIN; return (scalbn(x, in)); } float scalblnf (float x, long n) { int in; - in = (n > INT_MAX) ? INT_MAX : (n < INT_MIN) ? INT_MIN : n; + in = (int)n; + if (in != n) + in = (n > 0) ? INT_MAX: INT_MIN; return (scalbnf(x, in)); } long double scalblnl (long double x, long n) { int in; - in = (n > INT_MAX) ? INT_MAX : (n < INT_MIN) ? INT_MIN : n; + in = (int)n; + if (in != n) + in = (n > 0) ? INT_MAX: INT_MIN; return (scalbnl(x, in)); } Index: projects/building-blocks/release/doc/en_US.ISO8859-1/relnotes/article.xml =================================================================== --- projects/building-blocks/release/doc/en_US.ISO8859-1/relnotes/article.xml (revision 278195) +++ projects/building-blocks/release/doc/en_US.ISO8859-1/relnotes/article.xml (revision 278196) @@ -1,850 +1,967 @@ %release; %sponsor; %vendor; ]>
&os; &release.current; Release Notes The &os; Project $FreeBSD$ 2015 The &os; Documentation Project &tm-attrib.freebsd; &tm-attrib.ibm; &tm-attrib.ieee; &tm-attrib.intel; &tm-attrib.sparc; &tm-attrib.general; The release notes for &os; &release.current; contain a summary of the changes made to the &os; base system on the &release.branch; development line. This document lists applicable security advisories that were issued since the last release, as well as significant changes to the &os; kernel and userland. Some brief remarks on upgrading are also presented. Introduction This document contains the release notes for &os; &release.current;. It describes recently added, changed, or deleted features of &os;. It also provides some notes on upgrading from previous versions of &os;. The &release.type; distribution to which these release notes apply represents the latest point along the &release.branch; development branch since &release.branch; was created. Information regarding pre-built, binary &release.type; distributions along this branch can be found at &release.url;. The &release.type; distribution to which these release notes apply represents a point along the &release.branch; development branch between &release.prev; and the future &release.next;. Information regarding pre-built, binary &release.type; distributions along this branch can be found at &release.url;. This distribution of &os; &release.current; is a &release.type; distribution. It can be found at &release.url; or any of its mirrors. More information on obtaining this (or other) &release.type; distributions of &os; can be found in the Obtaining &os; appendix to the &os; Handbook. All users are encouraged to consult the release errata before installing &os;. The errata document is updated with late-breaking information discovered late in the release cycle or after the release. Typically, it contains information on known bugs, security advisories, and corrections to documentation. An up-to-date copy of the errata for &os; &release.current; can be found on the &os; Web site. - - - What's New - - This section describes the most user-visible new or changed + This document describes the most user-visible new or changed features in &os; since &release.prev;. In general, changes described here are unique to the &release.branch; branch unless specifically marked as &merged; features. Typical release note items document recent security advisories issued after &release.prev;, new drivers or hardware support, new commands or options, major bug fixes, or contributed software upgrades. They may also list changes to major ports/packages or release engineering practices. Clearly the release notes cannot list every single change made to &os; between releases; this document focuses primarily on security advisories, user-visible changes, and major architectural improvements. + - - Security Advisories + + Upgrading from Previous Releases of &os; - No advisories. + Binary upgrades between RELEASE versions + (and snapshots of the various security branches) are supported + using the &man.freebsd-update.8; utility. The binary upgrade + procedure will update unmodified userland utilities, as well as + unmodified GENERIC kernels distributed as a part of an official + &os; release. The &man.freebsd-update.8; utility requires that + the host being upgraded have Internet connectivity. - + Source-based upgrades (those based on recompiling the &os; + base system from source code) from previous versions are + supported, according to the instructions in + /usr/src/UPDATING. - - Kernel Changes + + Upgrading &os; should only be attempted after backing up + all data and configuration files. + + - Support for GPS ports has been added to - &man.uhso.4;. + + Security and Errata - The if_nf10bmac(4) - device has been added, providing support for NetFPGA-10G - Embedded CPU Ethernet Core. + This section lists the various Security Advisories and + Errata Notices since &release.prev;. - - The if_nf10bmac(4) driver operates on - the FPGA, and is not suited for the PCI host - interface. - + + Security Advisories - The &man.full.4; device has been added, - and the lindev(4) device has been removed. - Prior to this change, lindev(4) provided - only the /dev/full character device, - returning ENOSPC on write attempts. As - this device is not specific to &linux;, a native &os; version - has been added. + No advisories. - The &man.mpr.4; - device has been added, providing support for LSI Fusion-MPT - 3 12Gb SCSI/SATA controllers. + - The &man.mrsas.4; driver has been added, - providing support for LSI MegaRAID SAS controllers. The - &man.mfi.4; driver will attach to the controller, by default. - To enable &man.mrsas.4; add - hw.mfi.mrsas_enable=1 to - /boot/loader.conf, which turns off - &man.mfi.4; device probing. + + Errata Notices - - At this time, the &man.mfiutil.8; utility and the &os; - version of MegaCLI and - StorCli do not work with - &man.mrsas.4;. - + No errata notices. - A kernel bug that inhibited proper - functionality of the dev.cpu.0.freq - &man.sysctl.8; on &intel; processors with Turbo - Boost ™ enabled has been fixed. + + - The IMAGACT_BINMISC - kernel configuration option has been enabled by default, - which enables application execution through emulators, such - as Qemu. + + Userland - Support for the &man.cxgbe.4; Terminator - 5 (T5) 10G/40G cards has been added to &man.netmap.4;. + This section covers changes and additions to userland + applications, contributed software, and system utilities. - The VT kernel - configuration file has been removed, and the &man.vt.4; - driver is included in the GENERIC kernel. - To enable &man.vt.4;, enter set kern.vty=vt - at the &man.loader.8; prompt during boot, or add - kern.vty=vt to &man.loader.conf.5; and - reboot the system. + + Userland Configuration Changes - Support for - &man.dtrace.1; stack tracing has been fixed for - &os;/&arch.powerpc;, using the trapexit() - and asttrapexit() functions instead of - checking within addressed kernel space. + The default &man.newsyslog.conf.5; now + includes files in the + /etc/newsyslog.conf.d/ and + /usr/local/etc/newsyslog.conf.d/ + directories by default for &man.newsyslog.8;. - Hardware context support has been - added to the drm/i915 driver, adding - support for Mesa 9.2 and - later. + The &man.mailwrapper.8; utility has been + updated to use &man.mailer.conf.5; from the + LOCALBASE environment variable, which + defaults to /usr/local + if unset. - The &man.vt.4; driver has been updated, - replacing the bitmapped kern.vt.spclkeys - &man.sysctl.8; with individual - kern.vt.kbd_* variants. - - The &man.hpet.4; driver has been updated - to create a - /dev/hpetN - device, providing access to HPET from - userspace. - - The &man.vt.4; driver has been made the - default system console driver. The &man.syscons.4; driver is - still available, and can be enabled by adding - kern.vty=sc in &man.loader.conf.5;. - Alternatively, &man.syscons.4; can be enabled at boot time by - entering set kern.vty=sc at the - &man.loader.8; prompt. - - An issue that could cause a system to - hang when entering ACPI - S3 state (suspend to - RAM) has been corrected in the &man.acpi.4; - and &man.pci.4; drivers. - - The power management unit - subsystem has been updated to support power button events on - certain &arch.powerpc; hardware, such as aluminum - PowerBook ®. - - The &man.hwpmc.4; - driver has been updated to correct performance counter - sampling on G4 (MPC74xxx) and G5 class processors. - - The - OpenCrypto framework has been - updated to include AES-ICM and - AES-GCM modes, both of which have also been - added to the &man.aesni.4; driver. - - - Virtualization Support - - Support for the Virtual - Interrupt Delivery feature of &intel; VT-x is - enabled if supported by the CPU. This feature can be - disabled by running sysctl - hw.vmm.vmx.use_apic_vid=0. Additionally, to - persist this setting across reboots, add - hw.vmm.vmx.use_apic_vid=0 to - /etc/sysctl.conf. - - Support for Posted Interrupt - Processing is enabled if supported by the CPU. - This feature can be disabled by running sysctl - hw.vmm.vmx.use_apic_pir=0. Additionally, to - persist this setting across reboots, add - hw.vmm.vmx.use_apic_pir=0 to - /etc/sysctl.conf. - - Unmapped IO support has been added to - &man.virtio_blk.4;. - - Unmapped IO support has been added to - &man.virtio_scsi.4;. - - The &man.virtio_random.4; driver has - been added to harvest entropy from the host system. - - Support for running - a &os;/&arch.amd64; Xen guest - instance as PVH guest has been added. - PVH mode, short for - Para-Virtualized Hardware, uses - para-virtualized drivers for boot and I/O, and uses hardware - virtualization extensions for all other tasks, without the - need for emulation. - - The &man.virtio.console.4; driver has - been added, which provides an interface to VirtIO console - devices through a &man.tty.4; device. - - - - ARM Support - - The &man.nand.4; device is enabled for - ARM devices by default. - - An issue that could cause - instability when detecting SD cards on - the Raspberry Pi SOC has been - fixed. - - The bcm2835_cpufreq - driver has been added, which supports CPU - frequency and voltage control on the Raspberry Pi - SOC. - - - - Boot Loader Changes - -   - - - - Hardware Support - - The &man.asmc.4; driver has been - updated to support the &apple; MacMini 3,1. - - Support for &os;/ia64 has been dropped - as of &os; 11. - - - Multimedia Support - -   - - - - Network Interface Support - - Support for Broadcom chipsets - BCM57764, BCM57767, BCM57782, BCM57786 and BCM57787 has - been added to &man.bge.4;. - - Support for the &intel; - Centrino™ Wireless-N 135 chipset has been - added. - - Firmware for &intel; Centrino™ - Wireless-N 105 devices has been added to the base - system. - - The deprecated nve(4) driver has - been removed. Users of NVIDIA nForce MCP network adapters - are advised to use the &man.nfe.4; driver instead, which - has been the default driver for this hardware since - &os; 7.0. - - The &man.ath.hal.4; driver has been - updated to support the Atheros AR1111 chipset. - - Support for the &intel; - Centrino™ Wireless-N 105 chipset has been - added. - - A bug in &man.ipfw.4; that could - potentially lead to a kernel panic when using - &man.dummynet.4; at layer 2 has been fixed. - - The &man.alc.4; driver has been - updated to support AR816x and AR817x ethernet - controllers. - - The &man.vxlan.4; driver has been - added, which creates a virtual Layer 2 (Ethernet) network - overlaid in a Layer 3 (IP/UDP) network. The &man.vxlan.4; - driver is analogous to &man.vlan.4;, but is designed to be - better suited for large, multiple-tenant datacenter - environments. - - The &man.gre.4; driver has been - significantly overhauled, and has been split into two - separate modules, &man.gre.4; and &man.me.4;. - - - - - Network Protocols - - Support for the IPX network transport - protocol has been removed, and will not be supported in - &os; 11 and later releases. - - Support for PLPMTUD - blackhole detection (RFC 4821) has been - added to the &man.tcp.4; stack, disabled by default. New - control tunables have been added: - - - - - - - - Tunable - Description - - - - - - net.inet.tcp.pmtud_blackhole_detection - Enables or disables PLPMTUD - blackhole detection - - - - net.inet.tcp.pmtud_blackhole_mss - MSS to try for IPv4 - - - - net.inet.tcp.v6pmtud_blackhole_mss - MSS to try for IPv6 - - - - - - New monitoring &man.sysctl.8;s haven been added: - - - - - - - - Tunable - Description - - - - - - net.inet.tcp.pmtud_blackhole_activated - Number of times the code was activated to - attempt downshifting the - MSS - - - - net.inet.tcp.pmtud_blackhole_min_activated - Number of times the blackhole - MSS was used in an attempt to - downshift - - - - net.inet.tcp.pmtud_blackhole_failed - Number of times that the blackhole failed to - connect after downshifting the - MSS - - - - - - - - Disks and Storage - - Support for the - disklabel64 partitioning scheme has been - added to &man.gpart.8;. - - The asr(4) driver - has been removed, and is no longer supported. - - The - &man.ctl.4; subsystem has been updated, increasing the ports - limit from 128 to 256, - and LUN limit from 256 - to 1024. - - - - File Systems - - The - new filesystem automount facility, &man.autofs.5;, has been - added. The new &man.autofs.5; facility is similar to that - found in other &unix;-like operating systems, such as - OS X™ and Solaris™. The &man.autofs.5; - facility uses a &sun;-compatible &man.auto.master.5; - configuration file, and is administered with the - &man.automount.8; userland utility, and the - &man.automountd.8; and &man.autounmountd.8; daemons. - - - ZFS - - The - arc_meta_limit statistics are now - visible through the kstat - &man.sysctl.8;. As a result of this change, the - vfs.zfs.arc_meta_used &man.sysctl.8; - has been removed, and replaced with the - kstat.zfs.misc.arcstats.arc_meta_used - &man.sysctl.8;. - - + The MK_ARM_EABI + &man.src.conf.5; option has been removed. - - Userland Changes + + Userland Application Changes The &man.casperd.8; daemon has been added, which provides access to functionality that is not available in the capability mode sandbox. When unable to load a kernel module with &man.kldload.8;, a message informing to view output of &man.dmesg.8; is now printed, opposed to the previous output Exec format error.. Allow &man.pciconf.8; to identify PCI devices that are attached to a driver to be identified by their device name instead of just the selector. Additionally, an optional device argument to the -l flag to restrict the output to only listing details about a single device. A new flag, onifconsole has been added to /etc/ttys. This allows the system to provide a login prompt via serial console if the device is an active kernel console, otherwise it is equivalent to off. Support for displaying VPD for PCI devices via &man.pciconf.8; has been added. &man.ping.8; protects against malicious network packets using the Capsicum framework to drop privileges. - &os;/&arch.i386; guests can be run under - bhyve. - The &man.ps.1; utility has been updated to include the -J flag, used to filter output by matching &man.jail.8; IDs and names. Additionally, argument 0 can be used to -J to only list processes running on the host system. The &man.top.1; utility has been updated to filter by &man.jail.8; ID or name, in followup to the &man.ps.1; change in r265229. The Blowfish &man.crypt.3; default format has been changed to $2b$. The &man.pmcstat.8; utility has been updated to include a new flag, -l, which ends event collection after the specified number of seconds. - The default &man.newsyslog.conf.5; now - includes files in the - /etc/newsyslog.conf.d/ and - /usr/local/etc/newsyslog.conf.d/ - directories by default for &man.newsyslog.8;. - - The &man.readline.3; library is now - statically linked in software within the base system, and the - shared library is no longer installed, allowing the Ports - Collection to use a modern version of the library. - - The &man.mailwrapper.8; utility has been - updated to use &man.mailer.conf.5; from the - LOCALBASE environment variable, which - defaults to /usr/local - if unset. - The &man.ps.1; utility has been updated to include a new keyword, tracer, which displays the PID of the tracing process. Support for adding empty partitions has been added to the &man.mkimg.1; utility. - The &man.bsdinstall.8; partition editor - and &man.sade.8; utility have been updated to include native - ZFS support. - The &man.primes.6; utility has been updated to correctly enumerate prime numbers between 4295098369 and 3825123056546413050, which prior to this change, it would be possible for returned values to be incorrectly identified as prime numbers. The &man.mkimg.1; utility has been updated to include three options used to print information about &man.mkimg.1; itself: Option Output --version The current version of the &man.mkimg.1; utility --formats The disk image file formats supported by &man.mkimg.1; --schemes The partition schemes supported by &man.mkimg.1; - The &man.strptime.3; library has been - updated to add support for POSIX-2001 - features %U and - %W. - - The &os; installation utility, - &man.bsdinstall.8;, has been updated to set the - canmount &man.zfs.8; property to - off for the /var dataset, preventing the - contents of directories within /var from conflicting when - using multiple boot environments, such as that provided by - sysutils/beadm. - - The MK_ARM_EABI - &man.src.conf.5; option has been removed. - Userland &man.ctf.5; support in &man.dtrace.1; has been added. With this change, &man.dtrace.1; is able to resolve type info for function and USDT probe arguments, and function return values. - The &man.dl.iterate.phdr.3; library has been - changed to always return the path name of the - ELF object in the - dlpi_name structure member. - - A - userland library for Chelsio Terminator 5 based iWARP cards - has been added, allowing userland RDMA - applications to work over compatible - NICs. - - The &man.bsdconfig.8; utility has been - updated to skip the initial &man.tzsetup.8; - UTC versus wall-clock time prompt when run - in a virtual machine, determined when the - kern.vm_guest &man.sysctl.8; is set to - 1. - The &man.elfdump.1; utility has been updated to support capability mode provided by &man.capsicum.4;. - The &man.gpio.3; library has been added, - providing a wrapper around the &man.gpio.4; kernel - interface. - The &man.fstyp.8; utility has been added, which is used to determine the filesystem on a specified device. - The &man.bsdinstall.8; utility has been - updated to use the new &man.dpv.3; library to display progress - when extracting the &os; distributions. - The libedit library has been updated to support UTF-8, which additionally provides unicode support to &man.sh.1;. The &man.ptrace.2; system call has been updated include support for Altivec registers on &os;/&arch.powerpc;. - - - <acronym>ABI</acronym> Compatibility - - The &linux; compatibility version has - been updated to 2.6.18. The - compat.linux.osrelease &man.sysctl.8; is - evaluated when building the emulators/linux-c6 and related - ports. - - - - <filename>/etc/rc.d</filename> Scripts - - The &man.rc.8; subsystem has been - updated to allow configuring services in ${LOCALBASE}/etc/rc.conf.d/. - If LOCALBASE is unset, it defaults to - /usr/local. - - The mrouted - &man.rc.8; script has been removed from the base system. An - equivalent script is available from the net/mrouted port. - - - - <filename>/etc/periodic</filename> Scripts - - The daily &man.periodic.8; script - 110.clean-tmps has been updated to - avoid crossing filesystem mount boundaries when cleaning - files in /tmp. - - + Contributed Software &man.lldb.1; has been updated to upstream snapshot version r196259. Timezone data files have been updated to version 2013i. &man.byacc.1; has been updated to version 20140101. &man.jemalloc.3; has been updated to version 3.5.0. bmake has been updated to version 20140101. libc++ has been updated to version 3.4. OpenSSH has been updated to 6.5p1. mdocml has been updated to version 1.12.3. LLVM and Clang have been updated to version 3.4. Sendmail has been updated from 8.14.7 to 8.14.9. file has been updated to version 5.22. The binutils suite of utilities has been updated to include upstream patches that add new relocations for &arch.powerpc; support. The ELF Tool Chain has been updated to upstream revision r3136. The texinfo utility and info pages were removed from the base system. The print/texinfo port should be installed on systems where info pages are needed. The ELF object manipulation tools addr2line, elfcopy (strip), nm, size, and strings were switched to the versions from the ELF Tool Chain project. The libedit library has been updated to include UTF-8 support, adding UTF-8 support to the &man.sh.1; shell. OpenSSL has been updated to version 1.0.1l. - - Ports/Packages Collection Infrastructure + + Installation and Configuration Tools + The &man.bsdinstall.8; partition editor + and &man.sade.8; utility have been updated to include native + ZFS support. + + The &os; installation utility, + &man.bsdinstall.8;, has been updated to set the + canmount &man.zfs.8; property to + off for the /var dataset, preventing the + contents of directories within /var from conflicting when + using multiple boot environments, such as that provided by + sysutils/beadm. + + The &man.bsdconfig.8; utility has been + updated to skip the initial &man.tzsetup.8; + UTC versus wall-clock time prompt when run + in a virtual machine, determined when the + kern.vm_guest &man.sysctl.8; is set to + 1. + + The &man.bsdinstall.8; utility has been + updated to use the new &man.dpv.3; library to display progress + when extracting the &os; distributions. + + + + <filename class="directory">/etc/rc.d</filename> + Scripts + + The &man.rc.8; subsystem has been + updated to allow configuring services in ${LOCALBASE}/etc/rc.conf.d/. + If LOCALBASE is unset, it defaults to + /usr/local. + + The mrouted + &man.rc.8; script has been removed from the base system. An + equivalent script is available from the net/mrouted port. + + + + <filename class="directory">/etc/periodic</filename> + Scripts + + The daily &man.periodic.8; script + 110.clean-tmps has been updated to avoid + crossing filesystem mount boundaries when cleaning files in + /tmp. + + + + Runtime Libraries and API + + The &man.readline.3; library is now + statically linked in software within the base system, and the + shared library is no longer installed, allowing the Ports + Collection to use a modern version of the library. + + The &man.strptime.3; library has been + updated to add support for POSIX-2001 + features %U and + %W. + + The &man.dl.iterate.phdr.3; library has been + changed to always return the path name of the + ELF object in the + dlpi_name structure member. + + A + userland library for Chelsio Terminator 5 based iWARP cards + has been added, allowing userland RDMA + applications to work over compatible + NICs. + + The &man.gpio.3; library has been added, + providing a wrapper around the &man.gpio.4; kernel + interface. + + + + ABI Compatibility + + The &linux; compatibility version has + been updated to 2.6.18. The + compat.linux.osrelease &man.sysctl.8; is + evaluated when building the emulators/linux-c6 and related + ports. + + + + + Kernel + + This section covers changes to kernel configurations, system + tuning, and system control parameters that are not otherwise + categorized. + + + Kernel Bug Fixes + + A kernel bug that inhibited proper + functionality of the dev.cpu.0.freq + &man.sysctl.8; on &intel; processors with Turbo + Boost ™ enabled has been fixed. + + Support for + &man.dtrace.1; stack tracing has been fixed for + &os;/&arch.powerpc;, using the trapexit() + and asttrapexit() functions instead of + checking within addressed kernel space. + + A bug in &man.ipfw.4; that could + potentially lead to a kernel panic when using &man.dummynet.4; + at layer 2 has been fixed. + + + + Kernel Configuration + + The IMAGACT_BINMISC + kernel configuration option has been enabled by default, + which enables application execution through emulators, such + as Qemu. + + The VT kernel + configuration file has been removed, and the &man.vt.4; + driver is included in the GENERIC kernel. + To enable &man.vt.4;, enter set kern.vty=vt + at the &man.loader.8; prompt during boot, or add + kern.vty=vt to &man.loader.conf.5; and + reboot the system. + + + + System Tuning and Controls +   + - - Release Engineering and Integration + + Devices and Drivers - The - Release Engineering build tools have been updated to include - support for producing virtual machine disk images for various - cloud hosting providers. + This section covers changes and additions to devices and + device drivers since &release.prev;. + + + Device Drivers + + Support for GPS ports has been added to + &man.uhso.4;. + + The &man.full.4; device has been added, + and the lindev(4) device has been removed. + Prior to this change, lindev(4) provided + only the /dev/full character device, + returning ENOSPC on write attempts. As + this device is not specific to &linux;, a native &os; version + has been added. + + Hardware context support has been + added to the drm/i915 driver, adding + support for Mesa 9.2 and + later. + + The &man.vt.4; driver has been updated, + replacing the bitmapped kern.vt.spclkeys + &man.sysctl.8; with individual + kern.vt.kbd_* variants. + + The &man.hpet.4; driver has been updated + to create a + /dev/hpetN + device, providing access to HPET from + userspace. - - Documentation + + Storage Drivers + The &man.mpr.4; + device has been added, providing support for LSI Fusion-MPT + 3 12Gb SCSI/SATA controllers. + + The &man.mrsas.4; driver has been added, + providing support for LSI MegaRAID SAS controllers. The + &man.mfi.4; driver will attach to the controller, by default. + To enable &man.mrsas.4; add + hw.mfi.mrsas_enable=1 to + /boot/loader.conf, which turns off + &man.mfi.4; device probing. + + + At this time, the &man.mfiutil.8; utility and the &os; + version of MegaCLI and + StorCli do not work with + &man.mrsas.4;. + + + The + &man.ctl.4; subsystem has been updated, increasing the ports + limit from 128 to 256, + and LUN limit from 256 + to 1024. + + The asr(4) driver has + been removed, and is no longer supported. + + + + Network Drivers + + Support for Broadcom chipsets BCM57764, + BCM57767, BCM57782, BCM57786 and BCM57787 has been added to + &man.bge.4;. + + Support for the &intel; Centrino™ + Wireless-N 135 chipset has been added. + + Firmware for &intel; Centrino™ + Wireless-N 105 devices has been added to the base + system. + + The deprecated nve(4) driver has been + removed. Users of NVIDIA nForce MCP network adapters are + advised to use the &man.nfe.4; driver instead, which has been + the default driver for this hardware since + &os; 7.0. + + The if_nf10bmac(4) + device has been added, providing support for NetFPGA-10G + Embedded CPU Ethernet Core. + + + The if_nf10bmac(4) driver operates on + the FPGA, and is not suited for the PCI host + interface. + + + The &man.ath.hal.4; driver has been + updated to support the Atheros AR1111 chipset. + + Support for the &intel; Centrino™ + Wireless-N 105 chipset has been added. + + Support for the &man.cxgbe.4; Terminator + 5 (T5) 10G/40G cards has been added to &man.netmap.4;. + + The &man.alc.4; driver has been updated + to support AR816x and AR817x ethernet controllers. + + The &man.vxlan.4; driver has been added, + which creates a virtual Layer 2 (Ethernet) network overlaid in + a Layer 3 (IP/UDP) network. The &man.vxlan.4; driver is + analogous to &man.vlan.4;, but is designed to be better suited + for large, multiple-tenant datacenter environments. + + The + &man.gre.4; driver has been significantly overhauled, and has + been split into two separate modules, &man.gre.4; and + &man.me.4;. + + + + + Hardware Support + + This section covers general hardware support for physical + machines, hypervisors, and virtualization environments, as well + as hardware changes and updates that do not otherwise fit in + other sections of this document. + + + Hardware Support + + The &man.asmc.4; driver has been + updated to support the &apple; MacMini 3,1. + + Support for &os;/ia64 has been dropped + as of &os; 11. + + An issue that could cause a system to + hang when entering ACPI + S3 state (suspend to + RAM) has been corrected in the &man.acpi.4; + and &man.pci.4; drivers. + + The power management unit + subsystem has been updated to support power button events on + certain &arch.powerpc; hardware, such as aluminum + PowerBook ®. + + The &man.hwpmc.4; + driver has been updated to correct performance counter + sampling on G4 (MPC74xxx) and G5 class processors. + + The + OpenCrypto framework has been + updated to include AES-ICM and + AES-GCM modes, both of which have also been + added to the &man.aesni.4; driver. + + + + Virtualization Support + + Support for the Virtual Interrupt + Delivery feature of &intel; VT-x is enabled if + supported by the CPU. This feature can be disabled by running + sysctl hw.vmm.vmx.use_apic_vid=0. + Additionally, to persist this setting across reboots, add + hw.vmm.vmx.use_apic_vid=0 to + /etc/sysctl.conf. + + Support for Posted Interrupt + Processing is enabled if supported by the CPU. This + feature can be disabled by running sysctl + hw.vmm.vmx.use_apic_pir=0. Additionally, to + persist this setting across reboots, add + hw.vmm.vmx.use_apic_pir=0 to + /etc/sysctl.conf. + + Unmapped IO support has been added to + &man.virtio_blk.4;. + + Unmapped IO support has been added to + &man.virtio_scsi.4;. + + The &man.virtio_random.4; driver has + been added to harvest entropy from the host system. + + &os;/&arch.i386; guests can be run under + bhyve. + + Support for running a &os;/&arch.amd64; + Xen guest instance as + PVH guest has been added. + PVH mode, short for Para-Virtualized + Hardware, uses para-virtualized drivers for boot and + I/O, and uses hardware virtualization extensions for all other + tasks, without the need for emulation. + + The &man.virtio.console.4; driver has + been added, which provides an interface to VirtIO console + devices through a &man.tty.4; device. + + + + ARM Support + + The &man.nand.4; device is enabled for + ARM devices by default. + + An issue that could cause + instability when detecting SD cards on the + Raspberry Pi SOC has been fixed. + + The bcm2835_cpufreq + driver has been added, which supports CPU + frequency and voltage control on the Raspberry Pi + SOC. + + + + + Storage + + This section covers changes and additions to file systems + and other storage subsystems, both local and networked. + + + Networked Storage + + The new + filesystem automount facility, &man.autofs.5;, has been added. + The new &man.autofs.5; facility is similar to that found in + other &unix;-like operating systems, such as OS X™ + and Solaris™. The &man.autofs.5; facility uses + a &sun;-compatible &man.auto.master.5; configuration file, and + is administered with the &man.automount.8; userland utility, + and the &man.automountd.8; and &man.autounmountd.8; + daemons. + + + + ZFS + + The arc_meta_limit + statistics are now visible through the + kstat &man.sysctl.8;. As a result of this + change, the vfs.zfs.arc_meta_used + &man.sysctl.8; has been removed, and replaced with the + kstat.zfs.misc.arcstats.arc_meta_used + &man.sysctl.8;. + + + + &man.geom.4; + + Support for the + disklabel64 partitioning scheme has been + added to &man.gpart.8;. + + + + + Boot Loader Changes + + This section covers the boot loader, boot menu, and other + boot-related changes. + + + Boot Loader Changes + + The &man.vt.4; driver has been made the + default system console driver. The &man.syscons.4; driver is + still available, and can be enabled by adding + kern.vty=sc in &man.loader.conf.5;. + Alternatively, &man.syscons.4; can be enabled at boot time by + entering set kern.vty=sc at the + &man.loader.8; prompt. + + + + Boot Menu Changes +   - - Upgrading from Previous Releases of &os; + + Networking - Binary upgrades between RELEASE versions - (and snapshots of the various security branches) are supported - using the &man.freebsd-update.8; utility. The binary upgrade - procedure will update unmodified userland utilities, as well as - unmodified GENERIC kernels distributed as a part of an official - &os; release. The &man.freebsd-update.8; utility requires that - the host being upgraded have Internet connectivity. + This section describes changes that affect networking in + &os;. - Source-based upgrades (those based on recompiling the &os; - base system from source code) from previous versions are - supported, according to the instructions in - /usr/src/UPDATING. + + Network Procols - - Upgrading &os; should only be attempted after backing up - all data and configuration files. - + Support for the IPX network transport + protocol has been removed, and will not be supported in + &os; 11 and later releases. + + Support for PLPMTUD + blackhole detection (RFC 4821) has been + added to the &man.tcp.4; stack, disabled by default. New + control tunables have been added: + + + + + + + + Tunable + Description + + + + + + net.inet.tcp.pmtud_blackhole_detection + Enables or disables PLPMTUD + blackhole detection + + + + net.inet.tcp.pmtud_blackhole_mss + MSS to try for IPv4 + + + + net.inet.tcp.v6pmtud_blackhole_mss + MSS to try for IPv6 + + + + + + New monitoring &man.sysctl.8;s haven been added: + + + + + + + + Tunable + Description + + + + + + net.inet.tcp.pmtud_blackhole_activated + Number of times the code was activated to attempt + downshifting the MSS + + + + net.inet.tcp.pmtud_blackhole_min_activated + Number of times the blackhole + MSS was used in an attempt to + downshift + + + + net.inet.tcp.pmtud_blackhole_failed + Number of times that the blackhole failed to + connect after downshifting the + MSS + + + + + + + + + Ports Collection and Package Infrastructure + + This section covers changes to the &os; Ports + Collection, package infrastructure, and package maintenance and + installation tools. + + + Infrastructure Changes + +   + + + + Packaging Changes + +   + + + + + Documentation + + This section covers changes to the &os; Documentation + Project sources and toolchain. + + + Documentation Source Changes + +   + + + + Documentation Toolchain Changes + +   + + + + + Release Engineering and Integration + + This section convers changes that are specific to the + &os; Release Engineering processes. + + + Integration Changes + + The + Release Engineering build tools have been updated to include + support for producing virtual machine disk images for various + cloud hosting providers. +
Index: projects/building-blocks/release/doc/share/mk/doc.relnotes.mk =================================================================== --- projects/building-blocks/release/doc/share/mk/doc.relnotes.mk (revision 278195) +++ projects/building-blocks/release/doc/share/mk/doc.relnotes.mk (revision 278196) @@ -1,53 +1,54 @@ # $FreeBSD$ DOC_PREFIX?= ${RELN_ROOT}/../../../doc # XXX RELEASETYPE!= grep -o 'release.type "[a-z]*"' ${RELN_ROOT}/share/xml/release.ent | sed 's|[a-z.]* "\([a-z]*\)"|\1|' RELEASEURL!= grep -o 'release.url \"[^\"]*\"' ${RELN_ROOT}/share/xml/release.ent | sed 's|[^ ]* "\([^"]*\)"|\1|' RELEASEBRANCH!= grep -o 'release.branch "\([^"]*\)"' ${RELN_ROOT}/share/xml/release.ent | sed 's|[^ ]* "\([^"]*\)"|\1|' RELEASEMAILLIST!= grep -o 'release.maillist "\([^"]*\)"' ${RELN_ROOT}/share/xml/release.ent | sed 's|[^ ]* "\([^"]*\)"|\1|' .if ${RELEASETYPE} == "current" PROFILING+= --param profile.attribute "'releasetype'" --param profile.value "'current'" .elif ${RELEASETYPE} == "snapshot" PROFILING+= --param profile.attribute "'releasetype'" --param profile.value "'snapshot'" .elif ${RELEASETYPE} == "release" PROFILING+= --param profile.attribute "'releasetype'" --param profile.value "'release'" .endif XSLTPROCFLAGS+= --param release.url "'${RELEASEURL}'" XSLTPROCFLAGS+= --param release.branch "'${RELEASEBRANCH}'" XSLTPROCFLAGS+= --param release.maillist "'${RELEASEMAILLIST}'" +XSLTPROCFLAGS+= --param toc.section.depth "'3'" # Find the RELNOTESng document catalogs EXTRA_CATALOGS+= file://${RELN_ROOT}/${LANGCODE}/share/xml/catalog.xml \ file://${RELN_ROOT}/share/xml/catalog.xml XSLXHTML= http://www.FreeBSD.org/release/XML/share/xml/release.xsl # # Automatic device list generation: # .if exists(${RELN_ROOT}/../man4) MAN4DIR?= ${RELN_ROOT}/../man4 .elif exists(${RELN_ROOT}/../../man4) MAN4DIR?= ${RELN_ROOT}/../../man4 .else MAN4DIR?= ${RELN_ROOT}/../../share/man/man4 .endif MAN4PAGES?= ${MAN4DIR}/*.4 ${MAN4DIR}/man4.*/*.4 ARCHLIST?= ${RELN_ROOT}/share/misc/dev.archlist.txt DEV-AUTODIR= ${RELN_ROOT:S/${.CURDIR}/${.OBJDIR}/}/share/xml CLEANFILES+= ${DEV-AUTODIR}/dev-auto.ent MAN2HWNOTES_CMD=${RELN_ROOT}/share/misc/man2hwnotes.pl .if defined(HWNOTES_MI) MAN2HWNOTES_FLAGS= .else MAN2HWNOTES_FLAGS= -c .endif # Dependency that the article makefiles can use to pull in # dev-auto.ent. ${DEV-AUTODIR}/catalog-auto ${DEV-AUTODIR}/dev-auto.ent: ${MAN4PAGES} \ ${ARCHLIST} ${MAN2HWNOTES_CMD} cd ${RELN_ROOT}/share/xml && make MAN2HWNOTES_FLAGS=${MAN2HWNOTES_FLAGS} dev-auto.ent Index: projects/building-blocks/release/tools/gce.conf =================================================================== --- projects/building-blocks/release/tools/gce.conf (revision 278195) +++ projects/building-blocks/release/tools/gce.conf (revision 278196) @@ -1,100 +1,100 @@ #!/bin/sh # # $FreeBSD$ # # Set to a list of packages to install. export VM_EXTRA_PACKAGES="firstboot-freebsd-update firstboot-pkgs \ google-cloud-sdk google-daemon panicmail sudo firstboot-growfs \ google-startup-scripts" # Set to a list of third-party software to enable in rc.conf(5). -export VM_RC_LIST="google_accounts_manager ntpd" +export VM_RC_LIST="google_accounts_manager ntpd sshd firstboot_growfs \ + firstboot_pkgs google_startup" vm_extra_install_base() { echo 'search google.internal' > ${DESTDIR}/etc/resolv.conf echo 'nameserver 169.254.169.254' >> ${DESTDIR}/etc/resolv.conf echo 'nameserver 8.8.8.8' >> ${DESTDIR}/etc/resolv.conf } vm_extra_pre_umount() { cat << EOF >> ${DESTDIR}/etc/rc.conf dumpdev="AUTO" -ifconfig_vtnet0="SYNCDHCP mtu 1460" +ifconfig_DEFAULT="SYNCDHCP mtu 1460" ntpd_sync_on_start="YES" -ntpd_enable="YES" -sshd_enable="YES" -google_accounts_manager_enable="YES" -#disabled until I can figure out why the reboot for updates is hanging -#firstboot_freebsd_update_enable="YES" -#firstboot_pkgs_enable="YES" +case \$(uname -r) in + *-BETA*|*-RC*|*-RELEASE*) + firstboot_freebsd_update_enable="YES" + ;; + *) + ;; +esac # need to fill in something here #firstboot_pkgs_list="" panicmail_autosubmit="YES" -firstboot_growfs_enable="YES" -google_startup_enable="YES" EOF cat << EOF >> ${DESTDIR}/boot/loader.conf autoboot_delay="-1" beastie_disable="YES" loader_logo="none" hw.memtest.tests="0" console="comconsole,vidconsole" hw.vtnet.mq_disable=1 kern.timecounter.hardware=ACPI-safe aesni_load="YES" nvme_load="YES" EOF echo '169.254.169.254 metadata.google.internal metadata' > \ ${DESTDIR}/etc/hosts # overwrite ntp.conf cat << EOF > ${DESTDIR}/etc/ntp.conf server metadata.google.internal iburst restrict default kod nomodify notrap nopeer noquery restrict -6 default kod nomodify notrap nopeer noquery restrict 127.0.0.1 restrict -6 ::1 restrict 127.127.1.0 EOF cat << EOF >> ${DESTDIR}/etc/syslog.conf *.err;kern.warning;auth.notice;mail.crit /dev/console EOF cat << EOF >> ${DESTDIR}/etc/ssh/sshd_config ChallengeResponseAuthentication no X11Forwarding no AcceptEnv LANG Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc AllowAgentForwarding no ClientAliveInterval 420 EOF cat << EOF >> ${DESTDIR}/etc/crontab 0 3 * * * root /usr/sbin/freebsd-update cron EOF cat << EOF >> ${DESTDIR}/etc/sysctl.conf net.inet.icmp.drop_redirect=1 net.inet.ip.redirect=0 net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1 kern.ipc.somaxconn=1024 debug.trace_on_panic=1 debug.debugger_on_panic=0 EOF ## XXX: Verify this is needed. I do not see this requirement ## in the docs, and it impairs the ability to boot-test a copy ## of the image prior to packaging for upload to GCE. #sed -E -i '' 's/^([^#].*[[:space:]])on/\1off/' ${DESTDIR}/etc/ttys touch ${DESTDIR}/firstboot return 0 } Index: projects/building-blocks/sbin/Makefile =================================================================== --- projects/building-blocks/sbin/Makefile (revision 278195) +++ projects/building-blocks/sbin/Makefile (revision 278196) @@ -1,147 +1,143 @@ # @(#)Makefile 8.5 (Berkeley) 3/31/94 # $FreeBSD$ .include # XXX MISSING: icheck ncheck SUBDIR=adjkerntz \ badsect \ camcontrol \ clri \ comcontrol \ conscontrol \ ddb \ devfs \ dhclient \ dmesg \ dump \ dumpfs \ dumpon \ etherswitchcfg \ ffsinfo \ fsck \ fsck_ffs \ fsck_msdosfs \ fsdb \ fsirand \ gbde \ geom \ ggate \ growfs \ gvinum \ ifconfig \ init \ kldconfig \ kldload \ kldstat \ kldunload \ ldconfig \ md5 \ mdconfig \ mdmfs \ mknod \ mksnap_ffs \ mount \ mount_cd9660 \ mount_fusefs \ mount_msdosfs \ mount_nfs \ mount_nullfs \ mount_udf \ mount_unionfs \ newfs \ newfs_msdos \ nfsiod \ nos-tun \ ping \ rcorder \ reboot \ recoverdisk \ resolvconf \ restore \ route \ savecore \ setkey \ shutdown \ swapon \ sysctl \ tunefs \ umount .if ${MK_ATM} != "no" SUBDIR+= atm .endif .if ${MK_CASPER} != "no" SUBDIR+= casperd .endif .if ${MK_CCD} != "no" SUBDIR+= ccdconfig .endif .if ${MK_CXX} != "no" SUBDIR+= devd .endif .if ${MK_HAST} != "no" SUBDIR+= hastctl SUBDIR+= hastd .endif +.if ${MK_INET6} != "no" +SUBDIR+= ping6 +SUBDIR+= rtsol +.endif + .if ${MK_IPFILTER} != "no" SUBDIR+= ipf .endif .if ${MK_IPFW} != "no" SUBDIR+= ipfw SUBDIR+= natd .endif .if ${MK_ISCSI} != "no" SUBDIR+= iscontrol .endif .if ${MK_NAND} != "no" SUBDIR+= nandfs SUBDIR+= newfs_nandfs .endif .if ${MK_PF} != "no" SUBDIR+= pfctl SUBDIR+= pflogd .endif .if ${MK_PPP} != "no" SUBDIR+= spppcontrol -.endif - -.if ${MK_INET6} != "no" -SUBDIR+= ping6 -SUBDIR+= rtsol -.endif - -.if ${MK_ISCSI} != "no" -SUBDIR+= iscontrol .endif .if ${MK_QUOTAS} != "no" SUBDIR+= quotacheck .endif .if ${MK_ROUTED} != "no" SUBDIR+= routed .endif .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include SUBDIR:= ${SUBDIR:O} SUBDIR_PARALLEL= .include Index: projects/building-blocks/sbin/ifconfig/af_inet6.c =================================================================== --- projects/building-blocks/sbin/ifconfig/af_inet6.c (revision 278195) +++ projects/building-blocks/sbin/ifconfig/af_inet6.c (revision 278196) @@ -1,530 +1,536 @@ /* * Copyright (c) 1983, 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for struct ifaddr */ #include #include #include #include /* Define ND6_INFINITE_LIFETIME */ #include "ifconfig.h" static struct in6_ifreq in6_ridreq; -static struct in6_aliasreq in6_addreq = - { .ifra_flags = 0, +static struct in6_aliasreq in6_addreq = + { .ifra_flags = 0, .ifra_lifetime = { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; static int ip6lifetime; static int prefix(void *, int); static char *sec2str(time_t); static int explicit_prefix = 0; extern void setnd6flags(const char *, int, int, const struct afswtch *); extern void setnd6defif(const char *, int, int, const struct afswtch *); extern void nd6_status(int); static char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ static void setifprefixlen(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { if (afp->af_getprefix != NULL) afp->af_getprefix(addr, MASK); explicit_prefix = 1; } static void setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused, const struct afswtch *afp) { if (afp->af_af != AF_INET6) err(1, "address flags can be set only for inet6 addresses"); if (flag < 0) in6_addreq.ifra_flags &= ~(-flag); else in6_addreq.ifra_flags |= flag; } static void setip6lifetime(const char *cmd, const char *val, int s, const struct afswtch *afp) { struct timespec now; time_t newval; char *ep; clock_gettime(CLOCK_MONOTONIC_FAST, &now); newval = (time_t)strtoul(val, &ep, 0); if (val == ep) errx(1, "invalid %s", cmd); if (afp->af_af != AF_INET6) errx(1, "%s not allowed for the AF", cmd); if (strcmp(cmd, "vltime") == 0) { in6_addreq.ifra_lifetime.ia6t_expire = now.tv_sec + newval; in6_addreq.ifra_lifetime.ia6t_vltime = newval; } else if (strcmp(cmd, "pltime") == 0) { in6_addreq.ifra_lifetime.ia6t_preferred = now.tv_sec + newval; in6_addreq.ifra_lifetime.ia6t_pltime = newval; } } static void setip6pltime(const char *seconds, int dummy __unused, int s, const struct afswtch *afp) { setip6lifetime("pltime", seconds, s, afp); } static void setip6vltime(const char *seconds, int dummy __unused, int s, const struct afswtch *afp) { setip6lifetime("vltime", seconds, s, afp); } static void setip6eui64(const char *cmd, int dummy __unused, int s, const struct afswtch *afp) { struct ifaddrs *ifap, *ifa; const struct sockaddr_in6 *sin6 = NULL; const struct in6_addr *lladdr = NULL; struct in6_addr *in6; if (afp->af_af != AF_INET6) errx(EXIT_FAILURE, "%s not allowed for the AF", cmd); in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr; if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) errx(EXIT_FAILURE, "interface index is already filled"); if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, name) == 0) { sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { lladdr = &sin6->sin6_addr; break; } } } if (!lladdr) errx(EXIT_FAILURE, "could not determine link local address"); memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8); freeifaddrs(ifap); } static void in6_status(int s __unused, const struct ifaddrs *ifa) { struct sockaddr_in6 *sin, null_sin; struct in6_ifreq ifr6; int s6; u_int32_t flags6; struct in6_addrlifetime lifetime; struct timespec now; int error; clock_gettime(CLOCK_MONOTONIC_FAST, &now); memset(&null_sin, 0, sizeof(null_sin)); sin = (struct sockaddr_in6 *)ifa->ifa_addr; if (sin == NULL) return; strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_INET6,SOCK_DGRAM)"); return; } ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFAFLAG_IN6)"); close(s6); return; } flags6 = ifr6.ifr_ifru.ifru_flags6; memset(&lifetime, 0, sizeof(lifetime)); ifr6.ifr_addr = *sin; if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { warn("ioctl(SIOCGIFALIFETIME_IN6)"); close(s6); return; } lifetime = ifr6.ifr_ifru.ifru_lifetime; close(s6); error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("\tinet6 %s ", addr_buf); if (ifa->ifa_flags & IFF_POINTOPOINT) { sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr; /* * some of the interfaces do not have valid destination * address. */ if (sin != NULL && sin->sin6_family == AF_INET6) { int error; error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); if (error != 0) inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, sizeof(addr_buf)); printf("--> %s ", addr_buf); } } sin = (struct sockaddr_in6 *)ifa->ifa_netmask; if (sin == NULL) sin = &null_sin; printf("prefixlen %d ", prefix(&sin->sin6_addr, sizeof(struct in6_addr))); if ((flags6 & IN6_IFF_ANYCAST) != 0) printf("anycast "); if ((flags6 & IN6_IFF_TENTATIVE) != 0) printf("tentative "); if ((flags6 & IN6_IFF_DUPLICATED) != 0) printf("duplicated "); if ((flags6 & IN6_IFF_DETACHED) != 0) printf("detached "); if ((flags6 & IN6_IFF_DEPRECATED) != 0) printf("deprecated "); if ((flags6 & IN6_IFF_AUTOCONF) != 0) printf("autoconf "); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf("temporary "); if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) printf("prefer_source "); if (((struct sockaddr_in6 *)(ifa->ifa_addr))->sin6_scope_id) printf("scopeid 0x%x ", ((struct sockaddr_in6 *)(ifa->ifa_addr))->sin6_scope_id); if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { printf("pltime "); if (lifetime.ia6t_preferred) { printf("%s ", lifetime.ia6t_preferred < now.tv_sec - ? "0" : sec2str(lifetime.ia6t_preferred - now.tv_sec)); + ? "0" : + sec2str(lifetime.ia6t_preferred - now.tv_sec)); } else printf("infty "); printf("vltime "); if (lifetime.ia6t_expire) { printf("%s ", lifetime.ia6t_expire < now.tv_sec - ? "0" : sec2str(lifetime.ia6t_expire - now.tv_sec)); + ? "0" : + sec2str(lifetime.ia6t_expire - now.tv_sec)); } else printf("infty "); } print_vhid(ifa, " "); putchar('\n'); } #define SIN6(x) ((struct sockaddr_in6 *) &(x)) static struct sockaddr_in6 *sin6tab[] = { SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr) }; static void in6_getprefix(const char *plen, int which) { struct sockaddr_in6 *sin = sin6tab[which]; u_char *cp; int len = atoi(plen); if ((len < 0) || (len > 128)) errx(1, "%s: bad value", plen); sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if ((len == 0) || (len == 128)) { memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); return; } memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) *cp++ = 0xff; *cp = 0xff << (8 - len); } static void in6_getaddr(const char *s, int which) { struct sockaddr_in6 *sin = sin6tab[which]; struct addrinfo hints, *res; int error = -1; newaddr &= 1; sin->sin6_len = sizeof(*sin); if (which != MASK) sin->sin6_family = AF_INET6; if (which == ADDR) { char *p = NULL; if((p = strrchr(s, '/')) != NULL) { *p = '\0'; in6_getprefix(p + 1, MASK); explicit_prefix = 1; } } if (sin->sin6_family == AF_INET6) { bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; error = getaddrinfo(s, NULL, &hints, &res); } if (error != 0) { if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) errx(1, "%s: bad value", s); } else bcopy(res->ai_addr, sin, res->ai_addrlen); } static int prefix(void *val, int size) { - u_char *name = (u_char *)val; - int byte, bit, plen = 0; + u_char *name = (u_char *)val; + int byte, bit, plen = 0; - for (byte = 0; byte < size; byte++, plen += 8) - if (name[byte] != 0xff) - break; + for (byte = 0; byte < size; byte++, plen += 8) + if (name[byte] != 0xff) + break; if (byte == size) return (plen); for (bit = 7; bit != 0; bit--, plen++) - if (!(name[byte] & (1 << bit))) - break; - for (; bit != 0; bit--) - if (name[byte] & (1 << bit)) - return(0); - byte++; - for (; byte < size; byte++) - if (name[byte]) - return(0); - return (plen); + if (!(name[byte] & (1 << bit))) + break; + for (; bit != 0; bit--) + if (name[byte] & (1 << bit)) + return(0); + byte++; + for (; byte < size; byte++) + if (name[byte]) + return(0); + return (plen); } static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; if (0) { days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; p += sprintf(p, "%dd", days); } if (!first || hours) { first = 0; p += sprintf(p, "%dh", hours); } if (!first || mins) { first = 0; p += sprintf(p, "%dm", mins); } sprintf(p, "%ds", secs); } else sprintf(result, "%lu", (unsigned long)total); return(result); } static void in6_postproc(int s, const struct afswtch *afp) { if (explicit_prefix == 0) { /* Aggregatable address architecture defines all prefixes are 64. So, it is convenient to set prefixlen to 64 if it is not specified. */ setifprefixlen("64", 0, s, afp); /* in6_getprefix("64", MASK) if MASK is available here... */ } } static void in6_status_tunnel(int s) { char src[NI_MAXHOST]; char dst[NI_MAXHOST]; struct in6_ifreq in6_ifr; const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr; memset(&in6_ifr, 0, sizeof(in6_ifr)); strncpy(in6_ifr.ifr_name, name, IFNAMSIZ); if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0) src[0] = '\0'; if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0) return; if (sa->sa_family != AF_INET6) return; if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0) dst[0] = '\0'; printf("\ttunnel inet6 %s --> %s\n", src, dst); } static void in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) { struct in6_aliasreq in6_addreq; memset(&in6_addreq, 0, sizeof(in6_addreq)); strncpy(in6_addreq.ifra_name, name, IFNAMSIZ); memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len); memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len); if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) warn("SIOCSIFPHYADDR_IN6"); } static struct cmd inet6_cmds[] = { DEF_CMD_ARG("prefixlen", setifprefixlen), DEF_CMD("anycast", IN6_IFF_ANYCAST, setip6flags), DEF_CMD("tentative", IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("-tentative", -IN6_IFF_TENTATIVE, setip6flags), DEF_CMD("deprecated", IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("-no_radr", -ND6_IFF_NO_RADR, setnd6flags), DEF_CMD("defaultif", 1, setnd6defif), DEF_CMD("-defaultif", -1, setnd6defif), DEF_CMD("ifdisabled", ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags), DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags), DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags), DEF_CMD("no_prefer_iface",ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD("-no_prefer_iface",-ND6_IFF_NO_PREFER_IFACE,setnd6flags), DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), }; static struct afswtch af_inet6 = { .af_name = "inet6", .af_af = AF_INET6, .af_status = in6_status, .af_getaddr = in6_getaddr, .af_getprefix = in6_getprefix, .af_other_status = nd6_status, .af_postproc = in6_postproc, .af_status_tunnel = in6_status_tunnel, .af_settunnel = in6_set_tunnel, .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, .af_ridreq = &in6_addreq, .af_addreq = &in6_addreq, }; static void in6_Lopt_cb(const char *optarg __unused) { ip6lifetime++; /* print IPv6 address lifetime */ } -static struct option in6_Lopt = { .opt = "L", .opt_usage = "[-L]", .cb = in6_Lopt_cb }; +static struct option in6_Lopt = { + .opt = "L", + .opt_usage = "[-L]", + .cb = in6_Lopt_cb +}; static __constructor void inet6_ctor(void) { #define N(a) (sizeof(a) / sizeof(a[0])) size_t i; #ifndef RESCUE if (!feature_present("inet6")) return; #endif for (i = 0; i < N(inet6_cmds); i++) cmd_register(&inet6_cmds[i]); af_register(&af_inet6); opt_register(&in6_Lopt); #undef N } Index: projects/building-blocks/sbin/ifconfig/ifconfig.c =================================================================== --- projects/building-blocks/sbin/ifconfig/ifconfig.c (revision 278195) +++ projects/building-blocks/sbin/ifconfig/ifconfig.c (revision 278196) @@ -1,1231 +1,1401 @@ /* * Copyright (c) 1983, 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include +#include #include #include #include #include #include #include /* IP */ #include #include #include #include #include #include #include #include #include #ifdef JAIL #include #endif #include #include #include #include #include "ifconfig.h" /* * Since "struct ifreq" is composed of various union members, callers * should pay special attention to interpret the value. * (.e.g. little/big endian difference in the structure.) */ struct ifreq ifr; char name[IFNAMSIZ]; char *descr = NULL; size_t descrlen = 64; int setaddr; int setmask; int doalias; int clearaddr; int newaddr = 1; int verbose; int noload; int supmedia = 0; int printkeys = 0; /* Print keying material for interfaces. */ static int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp); static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, struct ifaddrs *ifa); static void tunnel_status(int s); static void usage(void); static struct afswtch *af_getbyname(const char *name); static struct afswtch *af_getbyfamily(int af); static void af_other_status(int); static struct option *opts = NULL; +struct ifa_order_elt { + int if_order; + int af_orders[255]; + struct ifaddrs *ifa; + TAILQ_ENTRY(ifa_order_elt) link; +}; + +TAILQ_HEAD(ifa_queue, ifa_order_elt); + void opt_register(struct option *p) { p->next = opts; opts = p; } static void usage(void) { char options[1024]; struct option *p; /* XXX not right but close enough for now */ options[0] = '\0'; for (p = opts; p != NULL; p = p->next) { strlcat(options, p->opt_usage, sizeof(options)); strlcat(options, " ", sizeof(options)); } fprintf(stderr, "usage: ifconfig %sinterface address_family [address [dest_address]]\n" " [parameters]\n" " ifconfig interface create\n" " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" " ifconfig -l [-d] [-u] [address_family]\n" " ifconfig %s[-d] [-m] [-u] [-v]\n", options, options, options); exit(1); } +#define ORDERS_SIZE(x) sizeof(x) / sizeof(x[0]) + +static int +calcorders(struct ifaddrs *ifa, struct ifa_queue *q) +{ + struct ifaddrs *prev; + struct ifa_order_elt *cur; + unsigned int ord, af, ifa_ord; + + prev = NULL; + cur = NULL; + ord = 0; + ifa_ord = 0; + + while (ifa != NULL) { + if (prev == NULL || + strcmp(ifa->ifa_name, prev->ifa_name) != 0) { + cur = calloc(1, sizeof(*cur)); + + if (cur == NULL) + return (-1); + + TAILQ_INSERT_TAIL(q, cur, link); + cur->if_order = ifa_ord ++; + cur->ifa = ifa; + ord = 0; + } + + if (ifa->ifa_addr) { + af = ifa->ifa_addr->sa_family; + + if (af < ORDERS_SIZE(cur->af_orders) && + cur->af_orders[af] == 0) + cur->af_orders[af] = ++ord; + } + prev = ifa; + ifa = ifa->ifa_next; + } + + return (0); +} + +static int +cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) +{ + struct ifa_order_elt *cur, *e1, *e2; + unsigned int af1, af2; + int ret; + + e1 = e2 = NULL; + + ret = strcmp(a->ifa_name, b->ifa_name); + if (ret != 0) { + TAILQ_FOREACH(cur, q, link) { + if (e1 && e2) + break; + + if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) + e1 = cur; + else if (strcmp(cur->ifa->ifa_name, b->ifa_name) == 0) + e2 = cur; + } + + if (!e1 || !e2) + return (0); + else + return (e1->if_order - e2->if_order); + + } else if (a->ifa_addr != NULL && b->ifa_addr != NULL) { + TAILQ_FOREACH(cur, q, link) { + if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) { + e1 = cur; + break; + } + } + + if (!e1) + return (0); + + af1 = a->ifa_addr->sa_family; + af2 = b->ifa_addr->sa_family; + + if (af1 < ORDERS_SIZE(e1->af_orders) && + af2 < ORDERS_SIZE(e1->af_orders)) + return (e1->af_orders[af1] - e1->af_orders[af2]); + } + + return (0); +} + +#undef ORDERS_SIZE + +static struct ifaddrs * +sortifaddrs(struct ifaddrs *list, + int (*compare)(struct ifaddrs *, struct ifaddrs *, struct ifa_queue *), + struct ifa_queue *q) +{ + struct ifaddrs *right, *temp, *last, *result, *next, *tail; + + right = list; + temp = list; + last = list; + result = NULL; + next = NULL; + tail = NULL; + + if (!list || !list->ifa_next) + return (list); + + while (temp && temp->ifa_next) { + last = right; + right = right->ifa_next; + temp = temp->ifa_next->ifa_next; + } + + last->ifa_next = NULL; + + list = sortifaddrs(list, compare, q); + right = sortifaddrs(right, compare, q); + + while (list || right) { + + if (!right) { + next = list; + list = list->ifa_next; + } else if (!list) { + next = right; + right = right->ifa_next; + } else if (compare(list, right, q) <= 0) { + next = list; + list = list->ifa_next; + } else { + next = right; + right = right->ifa_next; + } + + if (!result) + result = next; + else + tail->ifa_next = next; + + tail = next; + } + + return (result); +} + int main(int argc, char *argv[]) { int c, all, namesonly, downonly, uponly; const struct afswtch *afp = NULL; int ifindex; - struct ifaddrs *ifap, *ifa; + struct ifaddrs *ifap, *sifap, *ifa; struct ifreq paifr; const struct sockaddr_dl *sdl; char options[1024], *cp, *namecp = NULL; + struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); + struct ifa_order_elt *cur, *tmp; const char *ifname; struct option *p; size_t iflen; all = downonly = uponly = namesonly = noload = verbose = 0; /* Parse leading line options */ strlcpy(options, "adklmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { switch (c) { case 'a': /* scan all interfaces */ all++; break; case 'd': /* restrict scan to "down" interfaces */ downonly++; break; case 'k': printkeys++; break; case 'l': /* scan interface names only */ namesonly++; break; case 'm': /* show media choices in status */ supmedia = 1; break; case 'n': /* suppress module loading */ noload++; break; case 'u': /* restrict scan to "up" interfaces */ uponly++; break; case 'v': verbose++; break; default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { p->cb(optarg); break; } if (p == NULL) usage(); break; } } argc -= optind; argv += optind; /* -l cannot be used with -a or -m */ if (namesonly && (all || supmedia)) usage(); /* nonsense.. */ if (uponly && downonly) usage(); /* no arguments is equivalent to '-a' */ if (!namesonly && argc < 1) all = 1; /* -a and -l allow an address family arg to limit the output */ if (all || namesonly) { if (argc > 1) usage(); ifname = NULL; ifindex = 0; if (argc == 1) { afp = af_getbyname(*argv); if (afp == NULL) { warnx("Address family '%s' unknown.", *argv); usage(); } if (afp->af_name != NULL) argc--, argv++; /* leave with afp non-zero */ } } else { /* not listing, need an argument */ if (argc < 1) usage(); ifname = *argv; argc--, argv++; /* check and maybe load support for this interface */ ifmaybeload(ifname); ifindex = if_nametoindex(ifname); if (ifindex == 0) { /* * NOTE: We must special-case the `create' command * right here as we would otherwise fail when trying * to find the interface. */ if (argc > 0 && (strcmp(argv[0], "create") == 0 || strcmp(argv[0], "plumb") == 0)) { iflen = strlcpy(name, ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: cloning name too long", ifname); ifconfig(argc, argv, 1, NULL); exit(0); } #ifdef JAIL /* * NOTE: We have to special-case the `-vnet' command * right here as we would otherwise fail when trying * to find the interface as it lives in another vnet. */ if (argc > 0 && (strcmp(argv[0], "-vnet") == 0)) { iflen = strlcpy(name, ifname, sizeof(name)); if (iflen >= sizeof(name)) errx(1, "%s: interface name too long", ifname); ifconfig(argc, argv, 0, NULL); exit(0); } #endif errx(1, "interface %s does not exist", ifname); } } /* Check for address family */ if (argc > 0) { afp = af_getbyname(*argv); if (afp != NULL) argc--, argv++; } if (getifaddrs(&ifap) != 0) err(EXIT_FAILURE, "getifaddrs"); + cp = NULL; + + if (calcorders(ifap, &q) != 0) + err(EXIT_FAILURE, "calcorders"); + + sifap = sortifaddrs(ifap, cmpifaddrs, &q); + + TAILQ_FOREACH_SAFE(cur, &q, link, tmp) + free(cur); + ifindex = 0; - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + for (ifa = sifap; ifa; ifa = ifa->ifa_next) { memset(&paifr, 0, sizeof(paifr)); strncpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { memcpy(&paifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len); } if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0) continue; if (ifa->ifa_addr->sa_family == AF_LINK) sdl = (const struct sockaddr_dl *) ifa->ifa_addr; else sdl = NULL; if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0 && !namesonly) continue; iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); if (iflen >= sizeof(name)) { warnx("%s: interface name too long, skipping", ifa->ifa_name); continue; } cp = ifa->ifa_name; if ((ifa->ifa_flags & IFF_CANTCONFIG) != 0) continue; if (downonly && (ifa->ifa_flags & IFF_UP) != 0) continue; if (uponly && (ifa->ifa_flags & IFF_UP) == 0) continue; /* * Are we just listing the interfaces? */ if (namesonly) { if (namecp == cp) continue; if (afp != NULL) { /* special case for "ether" address family */ if (!strcmp(afp->af_name, "ether")) { if (sdl == NULL || (sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_L2VLAN && sdl->sdl_type != IFT_BRIDGE) || sdl->sdl_alen != ETHER_ADDR_LEN) continue; } else { - if (ifa->ifa_addr->sa_family != afp->af_af) + if (ifa->ifa_addr->sa_family + != afp->af_af) continue; } } namecp = cp; ifindex++; if (ifindex > 1) printf(" "); fputs(name, stdout); continue; } ifindex++; if (argc > 0) ifconfig(argc, argv, 0, afp); else status(afp, sdl, ifa); } if (namesonly) printf("\n"); freeifaddrs(ifap); exit(0); } static struct afswtch *afs = NULL; void af_register(struct afswtch *p) { p->af_next = afs; afs = p; } static struct afswtch * af_getbyname(const char *name) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (strcmp(afp->af_name, name) == 0) return afp; return NULL; } static struct afswtch * af_getbyfamily(int af) { struct afswtch *afp; for (afp = afs; afp != NULL; afp = afp->af_next) if (afp->af_af == af) return afp; return NULL; } static void af_other_status(int s) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_other_status == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_other_status(s); setbit(afmask, afp->af_af); } } static void af_all_tunnel_status(int s) { struct afswtch *afp; uint8_t afmask[howmany(AF_MAX, NBBY)]; memset(afmask, 0, sizeof(afmask)); for (afp = afs; afp != NULL; afp = afp->af_next) { if (afp->af_status_tunnel == NULL) continue; if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) continue; afp->af_status_tunnel(s); setbit(afmask, afp->af_af); } } static struct cmd *cmds = NULL; void cmd_register(struct cmd *p) { p->c_next = cmds; cmds = p; } static const struct cmd * cmd_lookup(const char *name, int iscreate) { #define N(a) (sizeof(a)/sizeof(a[0])) const struct cmd *p; for (p = cmds; p != NULL; p = p->c_next) if (strcmp(name, p->c_name) == 0) { if (iscreate) { if (p->c_iscloneop) return p; } else { if (!p->c_iscloneop) return p; } } return NULL; #undef N } struct callback { callback_func *cb_func; void *cb_arg; struct callback *cb_next; }; static struct callback *callbacks = NULL; void callback_register(callback_func *func, void *arg) { struct callback *cb; cb = malloc(sizeof(struct callback)); if (cb == NULL) errx(1, "unable to allocate memory for callback"); cb->cb_func = func; cb->cb_arg = arg; cb->cb_next = callbacks; callbacks = cb; } /* specially-handled commands */ static void setifaddr(const char *, int, int, const struct afswtch *); static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); static void setifdstaddr(const char *, int, int, const struct afswtch *); static const struct cmd setifdstaddr_cmd = DEF_CMD("ifdstaddr", 0, setifdstaddr); static int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp) { const struct afswtch *afp, *nafp; const struct cmd *p; struct callback *cb; int s; strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); afp = NULL; if (uafp != NULL) afp = uafp; /* * This is the historical "accident" allowing users to configure IPv4 * addresses without the "inet" keyword which while a nice feature has * proven to complicate other things. We cannot remove this but only * make sure we will never have a similar implicit default for IPv6 or * any other address familiy. We need a fallback though for * ifconfig IF up/down etc. to work without INET support as people * never used ifconfig IF link up/down, etc. either. */ #ifndef RESCUE #ifdef INET if (afp == NULL && feature_present("inet")) afp = af_getbyname("inet"); #endif #endif if (afp == NULL) afp = af_getbyname("link"); if (afp == NULL) { warnx("Please specify an address_family."); usage(); } top: ifr.ifr_addr.sa_family = afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? AF_LOCAL : afp->af_af; if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0 && (uafp != NULL || errno != EAFNOSUPPORT || (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); while (argc > 0) { p = cmd_lookup(*argv, iscreate); if (iscreate && p == NULL) { /* * Push the clone create callback so the new * device is created and can be used for any * remaining arguments. */ cb = callbacks; if (cb == NULL) errx(1, "internal error, no callback"); callbacks = cb->cb_next; cb->cb_func(s, cb->cb_arg); iscreate = 0; /* * Handle any address family spec that * immediately follows and potentially * recreate the socket. */ nafp = af_getbyname(*argv); if (nafp != NULL) { argc--, argv++; if (nafp != afp) { close(s); afp = nafp; goto top; } } /* * Look for a normal parameter. */ continue; } if (p == NULL) { /* * Not a recognized command, choose between setting * the interface address and the dst address. */ p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); } if (p->c_u.c_func || p->c_u.c_func2) { if (p->c_parameter == NEXTARG) { if (argv[1] == NULL) errx(1, "'%s' requires argument", p->c_name); p->c_u.c_func(argv[1], 0, s, afp); argc--, argv++; } else if (p->c_parameter == OPTARG) { p->c_u.c_func(argv[1], 0, s, afp); if (argv[1] != NULL) argc--, argv++; } else if (p->c_parameter == NEXTARG2) { if (argc < 3) errx(1, "'%s' requires 2 arguments", p->c_name); p->c_u.c_func2(argv[1], argv[2], s, afp); argc -= 2, argv += 2; } else p->c_u.c_func(*argv, p->c_parameter, s, afp); } argc--, argv++; } /* * Do any post argument processing required by the address family. */ if (afp->af_postproc != NULL) afp->af_postproc(s, afp); /* * Do deferred callbacks registered while processing * command-line arguments. */ for (cb = callbacks; cb != NULL; cb = cb->cb_next) cb->cb_func(s, cb->cb_arg); /* * Do deferred operations. */ if (clearaddr) { if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); clearaddr = 0; } } if (clearaddr) { int ret; strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name); ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); if (ret < 0) { if (errno == EADDRNOTAVAIL && (doalias >= 0)) { /* means no previous address for interface */ } else Perror("ioctl (SIOCDIFADDR)"); } } if (newaddr) { if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { warnx("interface %s cannot change %s addresses!", name, afp->af_name); newaddr = 0; } } if (newaddr && (setaddr || setmask)) { strncpy(afp->af_addreq, name, sizeof ifr.ifr_name); if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) Perror("ioctl (SIOCAIFADDR)"); } close(s); return(0); } /*ARGSUSED*/ static void setifaddr(const char *addr, int param, int s, const struct afswtch *afp) { if (afp->af_getaddr == NULL) return; /* * Delay the ioctl to set the interface addr until flags are all set. * The address interpretation may depend on the flags, * and the flags may change when the address is set. */ setaddr++; if (doalias == 0 && afp->af_af != AF_LINK) clearaddr = 1; afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); } static void settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) { struct addrinfo *srcres, *dstres; int ecode; if (afp->af_settunnel == NULL) { warn("address family %s does not support tunnel setup", afp->af_name); return; } if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); - if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) + if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) errx(1, "error in parsing address string: %s", gai_strerror(ecode)); if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) errx(1, "source and destination address families do not match"); afp->af_settunnel(s, srcres, dstres); freeaddrinfo(srcres); freeaddrinfo(dstres); } /* ARGSUSED */ static void deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) { if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) err(1, "SIOCDIFPHYADDR"); } #ifdef JAIL static void setifvnet(const char *jname, int dummy __unused, int s, const struct afswtch *afp) { struct ifreq my_ifr; memcpy(&my_ifr, &ifr, sizeof(my_ifr)); my_ifr.ifr_jid = jail_getid(jname); if (my_ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl(s, SIOCSIFVNET, &my_ifr) < 0) err(1, "SIOCSIFVNET"); } static void setifrvnet(const char *jname, int dummy __unused, int s, const struct afswtch *afp) { struct ifreq my_ifr; memcpy(&my_ifr, &ifr, sizeof(my_ifr)); my_ifr.ifr_jid = jail_getid(jname); if (my_ifr.ifr_jid < 0) errx(1, "%s", jail_errmsg); if (ioctl(s, SIOCSIFRVNET, &my_ifr) < 0) err(1, "SIOCSIFRVNET(%d, %s)", my_ifr.ifr_jid, my_ifr.ifr_name); } #endif static void setifnetmask(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) { setmask++; afp->af_getaddr(addr, MASK); } } static void setifbroadaddr(const char *addr, int dummy __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) afp->af_getaddr(addr, DSTADDR); } static void notealias(const char *addr, int param, int s, const struct afswtch *afp) { #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) if (setaddr && doalias == 0 && param < 0) if (afp->af_addreq != NULL && afp->af_ridreq != NULL) bcopy((caddr_t)rqtosa(af_addreq), (caddr_t)rqtosa(af_ridreq), rqtosa(af_addreq)->sa_len); doalias = param; if (param < 0) { clearaddr = 1; newaddr = 0; } else clearaddr = 0; #undef rqtosa } /*ARGSUSED*/ static void setifdstaddr(const char *addr, int param __unused, int s, const struct afswtch *afp) { if (afp->af_getaddr != NULL) afp->af_getaddr(addr, DSTADDR); } /* * Note: doing an SIOCIGIFFLAGS scribbles on the union portion * of the ifreq structure, which may confuse other parts of ifconfig. * Make a private copy so we can avoid that. */ static void setifflags(const char *vname, int value, int s, const struct afswtch *afp) { struct ifreq my_ifr; int flags; memset(&my_ifr, 0, sizeof(my_ifr)); (void) strlcpy(my_ifr.ifr_name, name, sizeof(my_ifr.ifr_name)); if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { Perror("ioctl (SIOCGIFFLAGS)"); exit(1); } flags = (my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16); if (value < 0) { value = -value; flags &= ~value; } else flags |= value; my_ifr.ifr_flags = flags & 0xffff; my_ifr.ifr_flagshigh = flags >> 16; if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) Perror(vname); } void setifcap(const char *vname, int value, int s, const struct afswtch *afp) { int flags; if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { Perror("ioctl (SIOCGIFCAP)"); exit(1); } flags = ifr.ifr_curcap; if (value < 0) { value = -value; flags &= ~value; } else flags |= value; flags &= ifr.ifr_reqcap; ifr.ifr_reqcap = flags; if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) Perror(vname); } static void setifmetric(const char *val, int dummy __unused, int s, const struct afswtch *afp) { strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_metric = atoi(val); if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) warn("ioctl (set metric)"); } static void setifmtu(const char *val, int dummy __unused, int s, const struct afswtch *afp) { strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_mtu = atoi(val); if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) warn("ioctl (set mtu)"); } static void setifname(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *newname; newname = strdup(val); if (newname == NULL) { warn("no memory to set ifname"); return; } ifr.ifr_data = newname; if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { warn("ioctl (set name)"); free(newname); return; } strlcpy(name, newname, sizeof(name)); free(newname); } /* ARGSUSED */ static void setifdescr(const char *val, int dummy __unused, int s, const struct afswtch *afp) { char *newdescr; ifr.ifr_buffer.length = strlen(val) + 1; if (ifr.ifr_buffer.length == 1) { ifr.ifr_buffer.buffer = newdescr = NULL; ifr.ifr_buffer.length = 0; } else { newdescr = strdup(val); ifr.ifr_buffer.buffer = newdescr; if (newdescr == NULL) { warn("no memory to set ifdescr"); return; } } if (ioctl(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) warn("ioctl (set descr)"); free(newdescr); } /* ARGSUSED */ static void unsetifdescr(const char *val, int value, int s, const struct afswtch *afp) { setifdescr("", 0, s, 0); } #define IFFBITS \ "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ "\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP" #define IFCAPBITS \ "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ "\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ "\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ "\26RXCSUM_IPV6\27TXCSUM_IPV6" /* * Print the status of the interface. If an address family was * specified, show only it; otherwise, show them all. */ static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, struct ifaddrs *ifa) { struct ifaddrs *ift; int allfamilies, s; struct ifstat ifs; if (afp == NULL) { allfamilies = 1; ifr.ifr_addr.sa_family = AF_LOCAL; } else { allfamilies = 0; ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_LOCAL : afp->af_af; } strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); if (s < 0) err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); printf("%s: ", name); printb("flags", ifa->ifa_flags, IFFBITS); if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) printf(" metric %d", ifr.ifr_metric); if (ioctl(s, SIOCGIFMTU, &ifr) != -1) printf(" mtu %d", ifr.ifr_mtu); putchar('\n'); for (;;) { if ((descr = reallocf(descr, descrlen)) != NULL) { ifr.ifr_buffer.buffer = descr; ifr.ifr_buffer.length = descrlen; if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) { if (ifr.ifr_buffer.buffer == descr) { if (strlen(descr) > 0) printf("\tdescription: %s\n", descr); } else if (ifr.ifr_buffer.length > descrlen) { descrlen = ifr.ifr_buffer.length; continue; } } } else warn("unable to allocate memory for interface" "description"); break; } if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { if (ifr.ifr_curcap != 0) { printb("\toptions", ifr.ifr_curcap, IFCAPBITS); putchar('\n'); } if (supmedia && ifr.ifr_reqcap != 0) { printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); putchar('\n'); } } tunnel_status(s); for (ift = ifa; ift != NULL; ift = ift->ifa_next) { if (ift->ifa_addr == NULL) continue; if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) continue; if (allfamilies) { const struct afswtch *p; p = af_getbyfamily(ift->ifa_addr->sa_family); if (p != NULL && p->af_status != NULL) p->af_status(s, ift); } else if (afp->af_af == ift->ifa_addr->sa_family) afp->af_status(s, ift); } #if 0 if (allfamilies || afp->af_af == AF_LINK) { const struct afswtch *lafp; /* * Hack; the link level address is received separately * from the routing information so any address is not * handled above. Cobble together an entry and invoke * the status method specially. */ lafp = af_getbyname("lladdr"); if (lafp != NULL) { info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; lafp->af_status(s, &info); } } #endif if (allfamilies) af_other_status(s); else if (afp->af_other_status != NULL) afp->af_other_status(s); strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); if (verbose > 0) sfp_status(s, &ifr, verbose); close(s); return; } static void tunnel_status(int s) { af_all_tunnel_status(s); } void Perror(const char *cmd) { switch (errno) { case ENXIO: errx(1, "%s: no such interface", cmd); break; case EPERM: errx(1, "%s: permission denied", cmd); break; default: err(1, "%s", cmd); } } /* * Print a value a la the %b format of the kernel's printf */ void printb(const char *s, unsigned v, const char *bits) { int i, any = 0; char c; if (bits && *bits == 8) printf("%s=%o", s, v); else printf("%s=%x", s, v); bits++; if (bits) { putchar('<'); while ((i = *bits++) != '\0') { if (v & (1 << (i-1))) { if (any) putchar(','); any = 1; for (; (c = *bits) > 32; bits++) putchar(c); } else for (; *bits > 32; bits++) ; } putchar('>'); } } void print_vhid(const struct ifaddrs *ifa, const char *s) { struct if_data *ifd; if (ifa->ifa_data == NULL) return; ifd = ifa->ifa_data; if (ifd->ifi_vhid == 0) return; printf("vhid %d ", ifd->ifi_vhid); } void ifmaybeload(const char *name) { #define MOD_PREFIX_LEN 3 /* "if_" */ struct module_stat mstat; int fileid, modid; char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; const char *cp; /* loading suppressed by the user */ if (noload) return; /* trim the interface number off the end */ strlcpy(ifname, name, sizeof(ifname)); for (dp = ifname; *dp != 0; dp++) if (isdigit(*dp)) { *dp = 0; break; } /* turn interface and unit into module name */ strcpy(ifkind, "if_"); strlcpy(ifkind + MOD_PREFIX_LEN, ifname, sizeof(ifkind) - MOD_PREFIX_LEN); /* scan files in kernel */ mstat.version = sizeof(struct module_stat); for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { /* scan modules in file */ for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) { if (modstat(modid, &mstat) < 0) continue; /* strip bus name if present */ if ((cp = strchr(mstat.name, '/')) != NULL) { cp++; } else { cp = mstat.name; } /* already loaded? */ if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 || strncmp(ifkind, cp, strlen(ifkind) + 1) == 0) return; } } /* not present, we should try to load it */ kldload(ifkind); } static struct cmd basic_cmds[] = { DEF_CMD("up", IFF_UP, setifflags), DEF_CMD("down", -IFF_UP, setifflags), DEF_CMD("arp", -IFF_NOARP, setifflags), DEF_CMD("-arp", IFF_NOARP, setifflags), DEF_CMD("debug", IFF_DEBUG, setifflags), DEF_CMD("-debug", -IFF_DEBUG, setifflags), DEF_CMD_ARG("description", setifdescr), DEF_CMD_ARG("descr", setifdescr), DEF_CMD("-description", 0, unsetifdescr), DEF_CMD("-descr", 0, unsetifdescr), DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), DEF_CMD("add", IFF_UP, notealias), DEF_CMD("alias", IFF_UP, notealias), DEF_CMD("-alias", -IFF_UP, notealias), DEF_CMD("delete", -IFF_UP, notealias), DEF_CMD("remove", -IFF_UP, notealias), #ifdef notdef #define EN_SWABIPS 0x1000 DEF_CMD("swabips", EN_SWABIPS, setifflags), DEF_CMD("-swabips", -EN_SWABIPS, setifflags), #endif DEF_CMD_ARG("netmask", setifnetmask), DEF_CMD_ARG("metric", setifmetric), DEF_CMD_ARG("broadcast", setifbroadaddr), DEF_CMD_ARG2("tunnel", settunnel), DEF_CMD("-tunnel", 0, deletetunnel), DEF_CMD("deletetunnel", 0, deletetunnel), #ifdef JAIL DEF_CMD_ARG("vnet", setifvnet), DEF_CMD_ARG("-vnet", setifrvnet), #endif DEF_CMD("link0", IFF_LINK0, setifflags), DEF_CMD("-link0", -IFF_LINK0, setifflags), DEF_CMD("link1", IFF_LINK1, setifflags), DEF_CMD("-link1", -IFF_LINK1, setifflags), DEF_CMD("link2", IFF_LINK2, setifflags), DEF_CMD("-link2", -IFF_LINK2, setifflags), DEF_CMD("monitor", IFF_MONITOR, setifflags), DEF_CMD("-monitor", -IFF_MONITOR, setifflags), DEF_CMD("staticarp", IFF_STATICARP, setifflags), DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("-rxcsum6", -IFCAP_RXCSUM_IPV6, setifcap), DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("-txcsum6", -IFCAP_TXCSUM_IPV6, setifcap), DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), DEF_CMD("netcons", IFCAP_NETCONS, setifcap), DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), DEF_CMD("polling", IFCAP_POLLING, setifcap), DEF_CMD("-polling", -IFCAP_POLLING, setifcap), DEF_CMD("tso6", IFCAP_TSO6, setifcap), DEF_CMD("-tso6", -IFCAP_TSO6, setifcap), DEF_CMD("tso4", IFCAP_TSO4, setifcap), DEF_CMD("-tso4", -IFCAP_TSO4, setifcap), DEF_CMD("tso", IFCAP_TSO, setifcap), DEF_CMD("-tso", -IFCAP_TSO, setifcap), DEF_CMD("toe", IFCAP_TOE, setifcap), DEF_CMD("-toe", -IFCAP_TOE, setifcap), DEF_CMD("lro", IFCAP_LRO, setifcap), DEF_CMD("-lro", -IFCAP_LRO, setifcap), DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", -IFCAP_WOL, setifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), DEF_CMD("normal", -IFF_LINK0, setifflags), DEF_CMD("compress", IFF_LINK0, setifflags), DEF_CMD("noicmp", IFF_LINK1, setifflags), DEF_CMD_ARG("mtu", setifmtu), DEF_CMD_ARG("name", setifname), }; static __constructor void ifconfig_ctor(void) { #define N(a) (sizeof(a) / sizeof(a[0])) size_t i; for (i = 0; i < N(basic_cmds); i++) cmd_register(&basic_cmds[i]); #undef N } Index: projects/building-blocks/sbin =================================================================== --- projects/building-blocks/sbin (revision 278195) +++ projects/building-blocks/sbin (revision 278196) Property changes on: projects/building-blocks/sbin ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sbin:r277872-278194 Index: projects/building-blocks/share/man/man5/src.conf.5 =================================================================== --- projects/building-blocks/share/man/man5/src.conf.5 (revision 278195) +++ projects/building-blocks/share/man/man5/src.conf.5 (revision 278196) @@ -1,1230 +1,1284 @@ .\" DO NOT EDIT-- this file is automatically generated. .\" from FreeBSD: head/tools/build/options/makeman 255964 2013-10-01 07:22:04Z des .\" $FreeBSD$ -.Dd January 25, 2015 +.Dd February 4, 2015 .Dt SRC.CONF 5 .Os .Sh NAME .Nm src.conf .Nd "source build options" .Sh DESCRIPTION The .Nm file contains settings that will apply to every build involving the .Fx source tree; see .Xr build 7 . .Pp The .Nm file uses the standard makefile syntax. However, .Nm should not specify any dependencies to .Xr make 1 . Instead, .Nm is to set .Xr make 1 variables that control the aspects of how the system builds. .Pp The default location of .Nm is .Pa /etc/src.conf , though an alternative location can be specified in the .Xr make 1 variable .Va SRCCONF . Overriding the location of .Nm may be necessary if the system-wide settings are not suitable for a particular build. For instance, setting .Va SRCCONF to .Pa /dev/null effectively resets all build controls to their defaults. .Pp The only purpose of .Nm is to control the compilation of the .Fx source code, which is usually located in .Pa /usr/src . As a rule, the system administrator creates .Nm when the values of certain control variables need to be changed from their defaults. .Pp In addition, control variables can be specified for a particular build via the .Fl D option of .Xr make 1 or in its environment; see .Xr environ 7 . .Pp The values of variables are ignored regardless of their setting; even if they would be set to .Dq Li FALSE or .Dq Li NO . Just the existence of an option will cause it to be honoured by .Xr make 1 . .Pp The following list provides a name and short description for variables that can be used for source builds. .Bl -tag -width indent .It Va WITHOUT_ACCT .\" from FreeBSD: head/tools/build/options/WITHOUT_ACCT 223201 2011-06-17 20:47:44Z ed Set to not build process accounting tools such as .Xr accton 8 and .Xr sa 8 . .It Va WITHOUT_ACPI .\" from FreeBSD: head/tools/build/options/WITHOUT_ACPI 156932 2006-03-21 07:50:50Z ru Set to not build .Xr acpiconf 8 , .Xr acpidump 8 and related programs. .It Va WITHOUT_AMD .\" from FreeBSD: head/tools/build/options/WITHOUT_AMD 183242 2008-09-21 22:02:26Z sam Set to not build .Xr amd 8 , and related programs. .It Va WITHOUT_APM .\" from FreeBSD: head/tools/build/options/WITHOUT_APM 183242 2008-09-21 22:02:26Z sam Set to not build .Xr apm 8 , .Xr apmd 8 and related programs. .It Va WITHOUT_ASSERT_DEBUG .\" from FreeBSD: head/tools/build/options/WITHOUT_ASSERT_DEBUG 162215 2006-09-11 13:55:27Z ru Set to compile programs and libraries without the .Xr assert 3 checks. .It Va WITHOUT_AT .\" from FreeBSD: head/tools/build/options/WITHOUT_AT 183242 2008-09-21 22:02:26Z sam Set to not build .Xr at 1 and related utilities. .It Va WITHOUT_ATM .\" from FreeBSD: head/tools/build/options/WITHOUT_ATM 156932 2006-03-21 07:50:50Z ru Set to not build programs and libraries related to ATM networking. .It Va WITHOUT_AUDIT .\" from FreeBSD: head/tools/build/options/WITHOUT_AUDIT 156932 2006-03-21 07:50:50Z ru Set to not build audit support into system programs. .It Va WITHOUT_AUTHPF .\" from FreeBSD: head/tools/build/options/WITHOUT_AUTHPF 156932 2006-03-21 07:50:50Z ru Set to not build .Xr authpf 8 . .It Va WITHOUT_AUTOFS .\" from FreeBSD: head/tools/build/options/WITHOUT_AUTOFS 277728 2015-01-26 07:15:49Z ngie Set to not build .Xr autofs 4 related programs, libraries, and kernel modules. .It Va WITHOUT_BHYVE .\" from FreeBSD: head/tools/build/options/WITHOUT_BHYVE 277727 2015-01-26 06:44:48Z ngie Set to not build or install .Xr bhyve 8 , associated utilities, and examples. .Pp This option only affects amd64/amd64. .It Va WITHOUT_BINUTILS .\" from FreeBSD: head/tools/build/options/WITHOUT_BINUTILS 266158 2014-05-15 16:51:45Z brooks Set to not build or install binutils (as, c++-filt, gconv, ld, nm, objcopy, objdump, readelf, size and strip) as part of the normal system build. The resulting system cannot build programs from source. .It Va WITHOUT_BINUTILS_BOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITHOUT_BINUTILS_BOOTSTRAP 264660 2014-04-18 17:03:58Z imp Set to not build binutils (as, c++-filt, gconv, ld, nm, objcopy, objdump, readelf, size and strip) as part of the bootstrap process. .Bf -symbolic The option does not work for build targets unless some alternative toolchain is provided. .Ef .It Va WITHOUT_BLUETOOTH .\" from FreeBSD: head/tools/build/options/WITHOUT_BLUETOOTH 156932 2006-03-21 07:50:50Z ru Set to not build Bluetooth related kernel modules, programs and libraries. .It Va WITHOUT_BOOT .\" from FreeBSD: head/tools/build/options/WITHOUT_BOOT 156932 2006-03-21 07:50:50Z ru Set to not build the boot blocks and loader. +.It Va WITHOUT_BOOTPARAMD +.\" from FreeBSD: head/tools/build/options/WITHOUT_BOOTPARAMD 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr bootparamd 8 . +.It Va WITHOUT_BOOTPD +.\" from FreeBSD: head/tools/build/options/WITHOUT_BOOTPD 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr bootpd 8 . .It Va WITHOUT_BSDINSTALL .\" from FreeBSD: head/tools/build/options/WITHOUT_BSDINSTALL 277677 2015-01-25 04:43:13Z ngie Set to not build .Xr bsdinstall 8 , .Xr sade 8 , and related programs. .It Va WITHOUT_BSD_CPIO .\" from FreeBSD: head/tools/build/options/WITHOUT_BSD_CPIO 179813 2008-06-16 05:48:15Z dougb Set to not build the BSD licensed version of cpio based on .Xr libarchive 3 . .It Va WITH_BSD_GREP .\" from FreeBSD: head/tools/build/options/WITH_BSD_GREP 222273 2011-05-25 01:04:12Z obrien Install BSD-licensed grep as '[ef]grep' instead of GNU grep. .It Va WITHOUT_BSNMP .\" from FreeBSD: head/tools/build/options/WITHOUT_BSNMP 183306 2008-09-23 16:15:42Z sam Set to not build or install .Xr bsnmpd 1 and related libraries and data files. .It Va WITHOUT_BZIP2 .\" from FreeBSD: head/tools/build/options/WITHOUT_BZIP2 174550 2007-12-12 16:43:17Z ru Set to not build contributed bzip2 software as a part of the base system. .Bf -symbolic The option has no effect yet. .Ef When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_BZIP2_SUPPORT .El .It Va WITHOUT_BZIP2_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_BZIP2_SUPPORT 166255 2007-01-26 10:19:08Z delphij Set to build some programs without optional bzip2 support. .It Va WITHOUT_CALENDAR .\" from FreeBSD: head/tools/build/options/WITHOUT_CALENDAR 156932 2006-03-21 07:50:50Z ru Set to not build .Xr calendar 1 . .It Va WITHOUT_CAPSICUM .\" from FreeBSD: head/tools/build/options/WITHOUT_CAPSICUM 229319 2012-01-02 21:57:58Z rwatson Set to not build Capsicum support into system programs. .It Va WITHOUT_CASPER .\" from FreeBSD: head/tools/build/options/WITHOUT_CASPER 258838 2013-12-02 08:21:28Z pjd Set to not build Casper program and related libraries. .It Va WITHOUT_CCD .\" from FreeBSD: head/tools/build/options/WITHOUT_CCD 277678 2015-01-25 04:52:48Z ngie Set to not build .Xr geom_ccd 4 and related utilities. .It Va WITHOUT_CDDL .\" from FreeBSD: head/tools/build/options/WITHOUT_CDDL 163861 2006-11-01 09:02:11Z jb Set to not build code licensed under Sun's CDDL. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_CTF .It .Va WITHOUT_ZFS .El .It Va WITHOUT_CLANG .\" from FreeBSD: head/tools/build/options/WITHOUT_CLANG 264660 2014-04-18 17:03:58Z imp Set to not build the Clang C/C++ compiler during the regular phase of the build. .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32 and sparc64/sparc64. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_CLANG_EXTRAS .It .Va WITHOUT_CLANG_FULL .El .It Va WITH_CLANG .\" from FreeBSD: head/tools/build/options/WITH_CLANG 264660 2014-04-18 17:03:58Z imp Set to build the Clang C/C++ compiler during the normal phase of the build. .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386, pc98/i386, powerpc/powerpc and powerpc/powerpc64. .It Va WITHOUT_CLANG_BOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITHOUT_CLANG_BOOTSTRAP 273177 2014-10-16 18:28:11Z skreuzer Set to not build the Clang C/C++ compiler during the bootstrap phase of the build. You must enable either gcc or clang bootstrap to be able to build the system, unless an alternative compiler is provided via XCC. .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32, powerpc/powerpc, powerpc/powerpc64 and sparc64/sparc64. .It Va WITH_CLANG_BOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITH_CLANG_BOOTSTRAP 264660 2014-04-18 17:03:58Z imp Set to build the Clang C/C++ compiler during the bootstrap phase of the build. .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386 and pc98/i386. .It Va WITH_CLANG_EXTRAS .\" from FreeBSD: head/tools/build/options/WITH_CLANG_EXTRAS 231057 2012-02-05 23:56:22Z dim Set to build additional clang and llvm tools, such as bugpoint. .It Va WITHOUT_CLANG_FULL .\" from FreeBSD: head/tools/build/options/WITHOUT_CLANG_FULL 246259 2013-02-02 22:28:29Z dim Set to avoid building the ARCMigrate, Rewriter and StaticAnalyzer components of the Clang C/C++ compiler. .Pp It is a default setting on arm/arm, arm/armeb, arm/armv6, arm/armv6hf, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32 and sparc64/sparc64. .It Va WITH_CLANG_FULL .\" from FreeBSD: head/tools/build/options/WITH_CLANG_FULL 246259 2013-02-02 22:28:29Z dim Set to build the ARCMigrate, Rewriter and StaticAnalyzer components of the Clang C/C++ compiler. .Pp It is a default setting on amd64/amd64, i386/i386, pc98/i386, powerpc/powerpc and powerpc/powerpc64. .It Va WITHOUT_CLANG_IS_CC .\" from FreeBSD: head/tools/build/options/WITHOUT_CLANG_IS_CC 242629 2012-11-05 21:53:23Z brooks Set to install the GCC compiler as .Pa /usr/bin/cc , .Pa /usr/bin/c++ and .Pa /usr/bin/cpp . .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32, powerpc/powerpc, powerpc/powerpc64 and sparc64/sparc64. .It Va WITH_CLANG_IS_CC .\" from FreeBSD: head/tools/build/options/WITH_CLANG_IS_CC 235342 2012-05-12 16:12:36Z gjb Set to install the Clang C/C++ compiler as .Pa /usr/bin/cc , .Pa /usr/bin/c++ and .Pa /usr/bin/cpp . .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386 and pc98/i386. .It Va WITHOUT_CPP .\" from FreeBSD: head/tools/build/options/WITHOUT_CPP 156932 2006-03-21 07:50:50Z ru Set to not build .Xr cpp 1 . .It Va WITHOUT_CROSS_COMPILER .\" from FreeBSD: head/tools/build/options/WITHOUT_CROSS_COMPILER 275138 2014-11-26 20:43:09Z gjb Set to not build any cross compiler in the cross-tools stage of buildworld. If you are compiling a different version of .Fx than what is installed on the system, you will need to provide an alternate compiler with XCC to ensure success. If you are compiling with an identical version of .Fx to the host, this option may be safely used. This option may also be safe when the host version of .Fx is close to the sources being built, but all bets are off if there have been any changes to the toolchain between the versions. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_BINUTILS_BOOTSTRAP .It .Va WITHOUT_CLANG_BOOTSTRAP .It .Va WITHOUT_GCC_BOOTSTRAP .El .It Va WITHOUT_CRYPT .\" from FreeBSD: head/tools/build/options/WITHOUT_CRYPT 156932 2006-03-21 07:50:50Z ru Set to not build any crypto code. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_KERBEROS .It .Va WITHOUT_KERBEROS_SUPPORT .It .Va WITHOUT_OPENSSH .It .Va WITHOUT_OPENSSL .El .Pp When set, the following options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_GSSAPI (unless .Va WITH_GSSAPI is set explicitly) .El .It Va WITH_CTF .\" from FreeBSD: head/tools/build/options/WITH_CTF 228159 2011-11-30 18:22:44Z fjoe Set to compile with CTF (Compact C Type Format) data. CTF data encapsulates a reduced form of debugging information similar to DWARF and the venerable stabs and is required for DTrace. .It Va WITHOUT_CTM .\" from FreeBSD: head/tools/build/options/WITHOUT_CTM 183242 2008-09-21 22:02:26Z sam Set to not build .Xr ctm 1 and related utilities. .It Va WITHOUT_CUSE .\" from FreeBSD: head/tools/build/options/WITHOUT_CUSE 270171 2014-08-19 15:40:26Z hselasky Set to not build CUSE-related programs and libraries. .It Va WITHOUT_CXX .\" from FreeBSD: head/tools/build/options/WITHOUT_CXX 220402 2011-04-06 20:19:07Z uqs Set to not build .Xr g++ 1 and related libraries. It will also prevent building of .Xr gperf 1 and .Xr devd 8 . When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_CLANG .It .Va WITHOUT_CLANG_EXTRAS .It .Va WITHOUT_CLANG_FULL .It .Va WITHOUT_GNUCXX .It .Va WITHOUT_GROFF .El .It Va WITH_DEBUG_FILES .\" from FreeBSD: head/tools/build/options/WITH_DEBUG_FILES 251512 2013-06-07 21:40:02Z emaste Set to strip debug info into a separate file for each executable binary and shared library. The debug files will be placed in a subdirectory of .Pa /usr/lib/debug and are located automatically by .Xr gdb 1 . .It Va WITHOUT_DICT .\" from FreeBSD: head/tools/build/options/WITHOUT_DICT 156932 2006-03-21 07:50:50Z ru Set to not build the Webster dictionary files. .It Va WITHOUT_DMAGENT .\" from FreeBSD: head/tools/build/options/WITHOUT_DMAGENT 262335 2014-02-22 13:05:23Z bapt Set to not build dma Mail Transport Agent .It Va WITHOUT_DOCCOMPRESS .\" from FreeBSD: head/tools/build/options/WITHOUT_DOCCOMPRESS 266752 2014-05-27 15:52:27Z gjb Set to not to install compressed system documentation. Only the uncompressed version will be installed. .It Va WITHOUT_DYNAMICROOT .\" from FreeBSD: head/tools/build/options/WITHOUT_DYNAMICROOT 156932 2006-03-21 07:50:50Z ru Set this if you do not want to link .Pa /bin and .Pa /sbin dynamically. .It Va WITHOUT_ED_CRYPTO .\" from FreeBSD: head/tools/build/options/WITHOUT_ED_CRYPTO 235660 2012-05-19 20:05:27Z marcel Set to build .Xr ed 1 without support for encryption/decryption. .It Va WITHOUT_EE .\" from FreeBSD: head/tools/build/options/WITHOUT_EE 277663 2015-01-25 00:03:44Z ngie Set to not build and install .Xr edit 1 , .Xr ee 1 , and related programs. .It Va WITH_EISA .\" from FreeBSD: head/tools/build/options/WITH_EISA 264654 2014-04-18 16:53:06Z imp Set to build EISA kernel modules. .It Va WITHOUT_ELFTOOLCHAIN_TOOLS .\" from FreeBSD: head/tools/build/options/WITHOUT_ELFTOOLCHAIN_TOOLS 276796 2015-01-07 22:02:37Z emaste Set to use .Xr addr2line 1 , .Xr nm 1 , .Xr size 1 , .Xr strings 1 , and .Xr strip 1 from GNU binutils instead of the ELF Tool Chain project. .It Va WITHOUT_EXAMPLES .\" from FreeBSD: head/tools/build/options/WITHOUT_EXAMPLES 156938 2006-03-21 09:06:24Z ru Set to avoid installing examples to .Pa /usr/share/examples/ . .It Va WITHOUT_FDT .\" from FreeBSD: head/tools/build/options/WITHOUT_FDT 221539 2011-05-06 19:10:27Z ru Set to not build Flattened Device Tree support as part of the base system. This includes the device tree compiler (dtc) and libfdt support library. +.It Va WITHOUT_FILE +.\" from FreeBSD: head/tools/build/options/WITHOUT_FILE 278193 2015-02-04 10:24:40Z ngie +Set to not build +.Xr file 1 +and related programs. +.It Va WITHOUT_FINGER +.\" from FreeBSD: head/tools/build/options/WITHOUT_FINGER 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr finger 1 +and +.Xr fingerd 8 . .It Va WITHOUT_FLOPPY .\" from FreeBSD: head/tools/build/options/WITHOUT_FLOPPY 221540 2011-05-06 19:13:03Z ru Set to not build or install programs for operating floppy disk driver. .It Va WITH_FMAKE .\" from FreeBSD: head/tools/build/options/WITH_FMAKE 275138 2014-11-26 20:43:09Z gjb Causes the old FreeBSD .Xr make 1 program to be built and installed as fmake. .It Va WITHOUT_FMTREE .\" from FreeBSD: head/tools/build/options/WITHOUT_FMTREE 261299 2014-01-30 21:37:43Z brooks Set to not build and install .Pa /usr/sbin/fmtree . .It Va WITHOUT_FORMAT_EXTENSIONS .\" from FreeBSD: head/tools/build/options/WITHOUT_FORMAT_EXTENSIONS 250658 2013-05-15 13:04:10Z brooks Set to not enable .Fl fformat-extensions when compiling the kernel. Also disables all format checking. .It Va WITHOUT_FORTH .\" from FreeBSD: head/tools/build/options/WITHOUT_FORTH 156932 2006-03-21 07:50:50Z ru Set to build bootloaders without Forth support. .It Va WITHOUT_FP_LIBC .\" from FreeBSD: head/tools/build/options/WITHOUT_FP_LIBC 156932 2006-03-21 07:50:50Z ru Set to build .Nm libc without floating-point support. .It Va WITHOUT_FREEBSD_UPDATE .\" from FreeBSD: head/tools/build/options/WITHOUT_FREEBSD_UPDATE 183242 2008-09-21 22:02:26Z sam Set to not build .Xr freebsd-update 8 . +.It Va WITHOUT_FTP +.\" from FreeBSD: head/tools/build/options/WITHOUT_FTP 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr ftp 1 +and +.Xr ftpd 8 . .It Va WITHOUT_GAMES .\" from FreeBSD: head/tools/build/options/WITHOUT_GAMES 156932 2006-03-21 07:50:50Z ru Set to not build games. .It Va WITHOUT_GCC .\" from FreeBSD: head/tools/build/options/WITHOUT_GCC 264660 2014-04-18 17:03:58Z imp Set to not build and install gcc and g++ as part of the normal build process. .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386 and pc98/i386. .It Va WITH_GCC .\" from FreeBSD: head/tools/build/options/WITH_GCC 255326 2013-09-06 20:49:48Z zeising Set to build and install gcc and g++. .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32, powerpc/powerpc, powerpc/powerpc64 and sparc64/sparc64. .It Va WITHOUT_GCC_BOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITHOUT_GCC_BOOTSTRAP 273177 2014-10-16 18:28:11Z skreuzer Set to not build gcc and g++ as part of the bootstrap process. You must enable either gcc or clang bootstrap to be able to build the system, unless an alternative compiler is provided via XCC. .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386 and pc98/i386. .It Va WITH_GCC_BOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITH_GCC_BOOTSTRAP 264660 2014-04-18 17:03:58Z imp Set to build gcc and g++ as part of the bootstrap process. .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32, powerpc/powerpc, powerpc/powerpc64 and sparc64/sparc64. .It Va WITHOUT_GCOV .\" from FreeBSD: head/tools/build/options/WITHOUT_GCOV 156932 2006-03-21 07:50:50Z ru Set to not build the .Xr gcov 1 tool. .It Va WITHOUT_GDB .\" from FreeBSD: head/tools/build/options/WITHOUT_GDB 156932 2006-03-21 07:50:50Z ru Set to not build .Xr gdb 1 . .It Va WITHOUT_GNU .\" from FreeBSD: head/tools/build/options/WITHOUT_GNU 174550 2007-12-12 16:43:17Z ru Set to not build contributed GNU software as a part of the base system. This option can be useful if the system built must not contain any code covered by the GNU Public License due to legal reasons. .Bf -symbolic The option has no effect yet. .Ef When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_GNU_SUPPORT .El .It Va WITHOUT_GNUCXX .\" from FreeBSD: head/tools/build/options/WITHOUT_GNUCXX 255321 2013-09-06 20:08:03Z theraven Do not build the GNU C++ stack (g++, libstdc++). This is the default on platforms where clang is the system compiler. .Pp It is a default setting on amd64/amd64, arm/arm, arm/armv6, arm/armv6hf, i386/i386 and pc98/i386. .It Va WITH_GNUCXX .\" from FreeBSD: head/tools/build/options/WITH_GNUCXX 255321 2013-09-06 20:08:03Z theraven Build the GNU C++ stack (g++, libstdc++). This is the default on platforms where gcc is the system compiler. .Pp It is a default setting on arm/armeb, mips/mipsel, mips/mips, mips/mips64el, mips/mips64, mips/mipsn32, powerpc/powerpc, powerpc/powerpc64 and sparc64/sparc64. .It Va WITHOUT_GNU_GREP_COMPAT .\" from FreeBSD: head/tools/build/options/WITHOUT_GNU_GREP_COMPAT 273421 2014-10-21 20:44:33Z emaste Set this option to omit the gnu extensions to grep from being included in BSD grep. .It Va WITHOUT_GNU_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_GNU_SUPPORT 156932 2006-03-21 07:50:50Z ru Set to build some programs without optional GNU support. .It Va WITHOUT_GPIO .\" from FreeBSD: head/tools/build/options/WITHOUT_GPIO 228081 2011-11-28 17:54:34Z dim Set to not build .Xr gpioctl 8 as part of the base system. .It Va WITHOUT_GPL_DTC .\" from FreeBSD: head/tools/build/options/WITHOUT_GPL_DTC 264515 2014-04-15 20:41:55Z imp Set to build the BSD licensed version of the device tree compiler, instead of the GPL'd one from elinux.org. .It Va WITHOUT_GROFF .\" from FreeBSD: head/tools/build/options/WITHOUT_GROFF 218941 2011-02-22 08:13:49Z uqs Set to not build .Xr groff 1 and .Xr vgrind 1 . You should consider installing the textproc/groff port to not break .Xr man 1 . .It Va WITHOUT_GSSAPI .\" from FreeBSD: head/tools/build/options/WITHOUT_GSSAPI 174548 2007-12-12 16:39:32Z ru Set to not build libgssapi. .It Va WITHOUT_HAST .\" from FreeBSD: head/tools/build/options/WITHOUT_HAST 277725 2015-01-26 06:27:07Z ngie Set to not build .Xr hastd 8 and related utilities. .It Va WITH_HESIOD .\" from FreeBSD: head/tools/build/options/WITH_HESIOD 156932 2006-03-21 07:50:50Z ru Set to build Hesiod support. .It Va WITHOUT_HTML .\" from FreeBSD: head/tools/build/options/WITHOUT_HTML 156932 2006-03-21 07:50:50Z ru Set to not build HTML docs. .It Va WITHOUT_HYPERV .\" from FreeBSD: head/tools/build/options/WITHOUT_HYPERV 271493 2014-09-13 02:15:31Z delphij Set to not build or install HyperV utilities. .It Va WITHOUT_ICONV .\" from FreeBSD: head/tools/build/options/WITHOUT_ICONV 254919 2013-08-26 17:15:56Z antoine Set to not build iconv as part of libc. .It Va WITHOUT_INCLUDES .\" from FreeBSD: head/tools/build/options/WITHOUT_INCLUDES 275138 2014-11-26 20:43:09Z gjb Set to not install header files. This option used to be spelled .Va NO_INCS . .Bf -symbolic The option does not work for build targets. .Ef .It Va WITHOUT_INET .\" from FreeBSD: head/tools/build/options/WITHOUT_INET 221266 2011-04-30 17:58:28Z bz Set to not build programs and libraries related to IPv4 networking. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_INET_SUPPORT .El .It Va WITHOUT_INET6 .\" from FreeBSD: head/tools/build/options/WITHOUT_INET6 156932 2006-03-21 07:50:50Z ru Set to not build programs and libraries related to IPv6 networking. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_INET6_SUPPORT .El .It Va WITHOUT_INET6_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_INET6_SUPPORT 156932 2006-03-21 07:50:50Z ru Set to build libraries, programs, and kernel modules without IPv6 support. +.It Va WITHOUT_INETD +.\" from FreeBSD: head/tools/build/options/WITHOUT_INETD 278192 2015-02-04 10:19:32Z ngie +Set to not build +.Xr inetd 8 . .It Va WITHOUT_INET_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_INET_SUPPORT 221266 2011-04-30 17:58:28Z bz Set to build libraries, programs, and kernel modules without IPv4 support. .It Va WITHOUT_INSTALLLIB .\" from FreeBSD: head/tools/build/options/WITHOUT_INSTALLLIB 174497 2007-12-09 21:56:21Z dougb Set this if you do not want to install optional libraries. For example when creating a .Xr nanobsd 8 image. .It Va WITH_INSTALL_AS_USER .\" from FreeBSD: head/tools/build/options/WITH_INSTALL_AS_USER 238021 2012-07-02 20:24:01Z marcel Set to make install targets succeed for non-root users by installing files with owner and group attributes set to that of the user running the .Xr make 1 command. The user still has to set the .Va DESTDIR variable to point to a directory where the user has write permissions. .It Va WITHOUT_IPFILTER .\" from FreeBSD: head/tools/build/options/WITHOUT_IPFILTER 156932 2006-03-21 07:50:50Z ru Set to not build IP Filter package. .It Va WITHOUT_IPFW .\" from FreeBSD: head/tools/build/options/WITHOUT_IPFW 183242 2008-09-21 22:02:26Z sam Set to not build IPFW tools. .It Va WITHOUT_ISCSI .\" from FreeBSD: head/tools/build/options/WITHOUT_ISCSI 277675 2015-01-25 04:20:11Z ngie Set to not build .Xr iscid 8 and related utilities. .It Va WITHOUT_JAIL .\" from FreeBSD: head/tools/build/options/WITHOUT_JAIL 249966 2013-04-27 04:09:09Z eadler Set to not build tools for the support of jails; e.g., .Xr jail 8 . .It Va WITHOUT_KDUMP .\" from FreeBSD: head/tools/build/options/WITHOUT_KDUMP 240690 2012-09-19 11:38:37Z zeising Set to not build .Xr kdump 1 and .Xr truss 1 . .It Va WITHOUT_KERBEROS .\" from FreeBSD: head/tools/build/options/WITHOUT_KERBEROS 174549 2007-12-12 16:42:03Z ru Set this if you do not want to build Kerberos 5 (KTH Heimdal). When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_KERBEROS_SUPPORT .El .Pp When set, the following options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_GSSAPI (unless .Va WITH_GSSAPI is set explicitly) .El .It Va WITHOUT_KERBEROS_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_KERBEROS_SUPPORT 251794 2013-06-15 20:29:07Z eadler Set to build some programs without Kerberos support, like .Xr ssh 1 , .Xr telnet 1 , .Xr sshd 8 , and .Xr telnetd 8 . .It Va WITHOUT_KERNEL_SYMBOLS .\" from FreeBSD: head/tools/build/options/WITHOUT_KERNEL_SYMBOLS 222189 2011-05-22 18:23:17Z imp Set to not install kernel symbol files. .Bf -symbolic This option is recommended for those people who have small root partitions. .Ef .It Va WITHOUT_KVM .\" from FreeBSD: head/tools/build/options/WITHOUT_KVM 174550 2007-12-12 16:43:17Z ru Set to not build the .Nm libkvm library as a part of the base system. .Bf -symbolic The option has no effect yet. .Ef When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_KVM_SUPPORT .El .It Va WITHOUT_KVM_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_KVM_SUPPORT 170644 2007-06-13 02:08:04Z sepotvin Set to build some programs without optional .Nm libkvm support. .It Va WITHOUT_LDNS .\" from FreeBSD: head/tools/build/options/WITHOUT_LDNS 255591 2013-09-15 13:11:13Z des Setting this variable will prevent the LDNS library from being built. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_LDNS_UTILS .It .Va WITHOUT_UNBOUND .El .It Va WITHOUT_LDNS_UTILS .\" from FreeBSD: head/tools/build/options/WITHOUT_LDNS_UTILS 255850 2013-09-24 14:33:31Z des Setting this variable will prevent building the LDNS utilities .Xr drill 1 and .Xr host 1 . .It Va WITHOUT_LEGACY_CONSOLE .\" from FreeBSD: head/tools/build/options/WITHOUT_LEGACY_CONSOLE 249966 2013-04-27 04:09:09Z eadler Set to not build programs that support a legacy PC console; e.g., .Xr kbdcontrol 8 and .Xr vidcontrol 8 . .It Va WITHOUT_LIB32 .\" from FreeBSD: head/tools/build/options/WITHOUT_LIB32 274664 2014-11-18 17:06:48Z imp On 64-bit platforms, set to not build 32-bit library set and a .Nm ld-elf32.so.1 runtime linker. .It Va WITHOUT_LIBCPLUSPLUS .\" from FreeBSD: head/tools/build/options/WITHOUT_LIBCPLUSPLUS 246262 2013-02-02 22:42:46Z dim Set to avoid building libcxxrt and libc++. .It Va WITHOUT_LIBPTHREAD .\" from FreeBSD: head/tools/build/options/WITHOUT_LIBPTHREAD 188848 2009-02-20 11:09:55Z mtm Set to not build the .Nm libpthread providing library, .Nm libthr . When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_LIBTHR .El .It Va WITHOUT_LIBTHR .\" from FreeBSD: head/tools/build/options/WITHOUT_LIBTHR 156932 2006-03-21 07:50:50Z ru Set to not build the .Nm libthr (1:1 threading) library. .It Va WITH_LLDB .\" from FreeBSD: head/tools/build/options/WITH_LLDB 255722 2013-09-20 01:52:02Z emaste Set to build the LLDB debugger. .It Va WITHOUT_LOCALES .\" from FreeBSD: head/tools/build/options/WITHOUT_LOCALES 156932 2006-03-21 07:50:50Z ru Set to not build localization files; see .Xr locale 1 . .It Va WITHOUT_LOCATE .\" from FreeBSD: head/tools/build/options/WITHOUT_LOCATE 183242 2008-09-21 22:02:26Z sam Set to not build .Xr locate 1 and related programs. .It Va WITHOUT_LPR .\" from FreeBSD: head/tools/build/options/WITHOUT_LPR 156932 2006-03-21 07:50:50Z ru Set to not build .Xr lpr 1 and related programs. .It Va WITHOUT_LS_COLORS .\" from FreeBSD: head/tools/build/options/WITHOUT_LS_COLORS 235660 2012-05-19 20:05:27Z marcel Set to build .Xr ls 1 without support for colors to distinguish file types. .It Va WITHOUT_LZMA_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_LZMA_SUPPORT 245171 2013-01-08 18:37:12Z obrien Set to build some programs without optional lzma compression support. .It Va WITHOUT_MAIL .\" from FreeBSD: head/tools/build/options/WITHOUT_MAIL 183242 2008-09-21 22:02:26Z sam Set to not build any mail support (MUA or MTA). When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_DMAGENT .It .Va WITHOUT_MAILWRAPPER .It .Va WITHOUT_SENDMAIL .El .It Va WITHOUT_MAILWRAPPER .\" from FreeBSD: head/tools/build/options/WITHOUT_MAILWRAPPER 156932 2006-03-21 07:50:50Z ru Set to not build the .Xr mailwrapper 8 MTA selector. .It Va WITHOUT_MAKE .\" from FreeBSD: head/tools/build/options/WITHOUT_MAKE 183242 2008-09-21 22:02:26Z sam Set to not install .Xr make 1 and related support files. .It Va WITHOUT_MAN .\" from FreeBSD: head/tools/build/options/WITHOUT_MAN 156932 2006-03-21 07:50:50Z ru Set to not build manual pages. When set, the following options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_MAN_UTILS (unless .Va WITH_MAN_UTILS is set explicitly) .El .It Va WITHOUT_MANCOMPRESS .\" from FreeBSD: head/tools/build/options/WITHOUT_MANCOMPRESS 266752 2014-05-27 15:52:27Z gjb Set to not to install compressed man pages. Only the uncompressed versions will be installed. .It Va WITHOUT_MAN_UTILS .\" from FreeBSD: head/tools/build/options/WITHOUT_MAN_UTILS 208322 2010-05-20 00:07:21Z jkim Set to not build utilities for manual pages, .Xr apropos 1 , .Xr catman 1 , .Xr makewhatis 1 , .Xr man 1 , .Xr whatis 1 , .Xr manctl 8 , and related support files. .It Va WITH_NAND .\" from FreeBSD: head/tools/build/options/WITH_NAND 235537 2012-05-17 10:11:18Z gber Set to build the NAND Flash components. .It Va WITHOUT_NDIS .\" from FreeBSD: head/tools/build/options/WITHOUT_NDIS 183242 2008-09-21 22:02:26Z sam Set to not build programs and libraries related to NDIS emulation support. .It Va WITHOUT_NETCAT .\" from FreeBSD: head/tools/build/options/WITHOUT_NETCAT 156932 2006-03-21 07:50:50Z ru Set to not build .Xr nc 1 utility. .It Va WITHOUT_NETGRAPH .\" from FreeBSD: head/tools/build/options/WITHOUT_NETGRAPH 183242 2008-09-21 22:02:26Z sam Set to not build applications to support .Xr netgraph 4 . When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_ATM .It .Va WITHOUT_BLUETOOTH .It .Va WITHOUT_NETGRAPH_SUPPORT .El .It Va WITHOUT_NETGRAPH_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_NETGRAPH_SUPPORT 183305 2008-09-23 16:11:15Z sam Set to build libraries, programs, and kernel modules without netgraph support. .It Va WITHOUT_NIS .\" from FreeBSD: head/tools/build/options/WITHOUT_NIS 156932 2006-03-21 07:50:50Z ru Set to not build .Xr NIS 8 support and related programs. If set, you might need to adopt your .Xr nsswitch.conf 5 and remove .Sq nis entries. .It Va WITHOUT_NLS .\" from FreeBSD: head/tools/build/options/WITHOUT_NLS 156932 2006-03-21 07:50:50Z ru Set to not build NLS catalogs. .It Va WITHOUT_NLS_CATALOGS .\" from FreeBSD: head/tools/build/options/WITHOUT_NLS_CATALOGS 156932 2006-03-21 07:50:50Z ru Set to not build NLS catalog support for .Xr csh 1 . .It Va WITHOUT_NS_CACHING .\" from FreeBSD: head/tools/build/options/WITHOUT_NS_CACHING 172803 2007-10-19 14:01:25Z ru Set to disable name caching in the .Pa nsswitch subsystem. The generic caching daemon, .Xr nscd 8 , will not be built either if this option is set. .It Va WITHOUT_NTP .\" from FreeBSD: head/tools/build/options/WITHOUT_NTP 183242 2008-09-21 22:02:26Z sam Set to not build .Xr ntpd 8 and related programs. .It Va WITH_OFED .\" from FreeBSD: head/tools/build/options/WITH_OFED 228081 2011-11-28 17:54:34Z dim Set to build the .Dq "OpenFabrics Enterprise Distribution" Infiniband software stack. .It Va WITH_OPENLDAP .\" from FreeBSD: head/tools/build/options/WITH_OPENLDAP 264902 2014-04-24 23:17:31Z imp Enable building openldap support for kerberos. .It Va WITHOUT_OPENSSH .\" from FreeBSD: head/tools/build/options/WITHOUT_OPENSSH 156932 2006-03-21 07:50:50Z ru Set to not build OpenSSH. .It Va WITH_OPENSSH_NONE_CIPHER .\" from FreeBSD: head/tools/build/options/WITH_OPENSSH_NONE_CIPHER 245527 2013-01-17 01:51:04Z bz Set to include the "None" cipher support in OpenSSH and its libraries. Additional adjustments may need to be done to system configuration files, such as .Xr sshd_config 5 , to enable this cipher. Please see .Pa /usr/src/crypto/openssh/README.hpn for full details. .It Va WITHOUT_OPENSSL .\" from FreeBSD: head/tools/build/options/WITHOUT_OPENSSL 156932 2006-03-21 07:50:50Z ru Set to not build OpenSSL. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_KERBEROS .It .Va WITHOUT_KERBEROS_SUPPORT .It .Va WITHOUT_OPENSSH .El .Pp When set, the following options are also in effect: .Pp .Bl -inset -compact .It Va WITHOUT_GSSAPI (unless .Va WITH_GSSAPI is set explicitly) .El .It Va WITHOUT_PAM .\" from FreeBSD: head/tools/build/options/WITHOUT_PAM 174550 2007-12-12 16:43:17Z ru Set to not build PAM library and modules. .Bf -symbolic This option is deprecated and does nothing. .Ef When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_PAM_SUPPORT .El .It Va WITHOUT_PAM_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_PAM_SUPPORT 156932 2006-03-21 07:50:50Z ru Set to build some programs without PAM support, particularly .Xr ftpd 8 and .Xr ppp 8 . .It Va WITHOUT_PC_SYSINSTALL .\" from FreeBSD: head/tools/build/options/WITHOUT_PC_SYSINSTALL 245606 2013-01-18 15:57:09Z eadler Set to not build .Xr pc-sysinstall 8 and related programs. .It Va WITHOUT_PF .\" from FreeBSD: head/tools/build/options/WITHOUT_PF 156932 2006-03-21 07:50:50Z ru Set to not build PF firewall package. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_AUTHPF .El .It Va WITHOUT_PKGBOOTSTRAP .\" from FreeBSD: head/tools/build/options/WITHOUT_PKGBOOTSTRAP 258924 2013-12-04 15:58:42Z bdrewery Set to not build .Xr pkg 7 bootstrap tool. .It Va WITHOUT_PMC .\" from FreeBSD: head/tools/build/options/WITHOUT_PMC 183242 2008-09-21 22:02:26Z sam Set to not build .Xr pmccontrol 8 and related programs. .It Va WITHOUT_PORTSNAP .\" from FreeBSD: head/tools/build/options/WITHOUT_PORTSNAP 183242 2008-09-21 22:02:26Z sam Set to not build or install .Xr portsnap 8 and related files. .It Va WITHOUT_PPP .\" from FreeBSD: head/tools/build/options/WITHOUT_PPP 183242 2008-09-21 22:02:26Z sam Set to not build .Xr ppp 8 and related programs. .It Va WITHOUT_PROFILE .\" from FreeBSD: head/tools/build/options/WITHOUT_PROFILE 228196 2011-12-02 09:09:54Z fjoe Set to avoid compiling profiled libraries. .It Va WITHOUT_QUOTAS .\" from FreeBSD: head/tools/build/options/WITHOUT_QUOTAS 183242 2008-09-21 22:02:26Z sam Set to not build .Xr quota 8 and related programs. +.It Va WITHOUT_RADIUS_SUPPORT +.\" from FreeBSD: head/tools/build/options/WITHOUT_RADIUS_SUPPORT 278182 2015-02-04 06:53:45Z ngie +Set to not build radius support into various applications, like +.Xr pam_radius 8 +and +.Xr ppp 8 . +.It Va WITHOUT_RBOOTD +.\" from FreeBSD: head/tools/build/options/WITHOUT_RBOOTD 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr rbootd 8 . .It Va WITHOUT_RCMDS .\" from FreeBSD: head/tools/build/options/WITHOUT_RCMDS 156932 2006-03-21 07:50:50Z ru Disable building of the .Bx r-commands. This includes .Xr rlogin 1 , .Xr rsh 1 , etc. .It Va WITHOUT_RCS .\" from FreeBSD: head/tools/build/options/WITHOUT_RCS 275138 2014-11-26 20:43:09Z gjb Set to not build .Xr rcs 1 , .Xr etcupdate 8 , and related utilities. .It Va WITHOUT_RESCUE .\" from FreeBSD: head/tools/build/options/WITHOUT_RESCUE 156932 2006-03-21 07:50:50Z ru Set to not build .Xr rescue 8 . .It Va WITHOUT_ROUTED .\" from FreeBSD: head/tools/build/options/WITHOUT_ROUTED 183242 2008-09-21 22:02:26Z sam Set to not build .Xr routed 8 utility. .It Va WITHOUT_SENDMAIL .\" from FreeBSD: head/tools/build/options/WITHOUT_SENDMAIL 156932 2006-03-21 07:50:50Z ru Set to not build .Xr sendmail 8 and related programs. .It Va WITHOUT_SETUID_LOGIN .\" from FreeBSD: head/tools/build/options/WITHOUT_SETUID_LOGIN 156932 2006-03-21 07:50:50Z ru Set this to disable the installation of .Xr login 1 as a set-user-ID root program. .It Va WITHOUT_SHAREDOCS .\" from FreeBSD: head/tools/build/options/WITHOUT_SHAREDOCS 156932 2006-03-21 07:50:50Z ru Set to not build the .Bx 4.4 legacy docs. .It Va WITH_SHARED_TOOLCHAIN .\" from FreeBSD: head/tools/build/options/WITH_SHARED_TOOLCHAIN 235342 2012-05-12 16:12:36Z gjb Set to build the toolchain binaries shared. The set includes .Xr cc 1 , .Xr make 1 and necessary utilities like assembler, linker and library archive manager. .It Va WITH_SORT_THREADS .\" from FreeBSD: head/tools/build/options/WITH_SORT_THREADS 264158 2014-04-05 18:00:45Z imp Set to enable threads in .Xr sort 1 . .It Va WITHOUT_SOURCELESS .\" from FreeBSD: head/tools/build/options/WITHOUT_SOURCELESS 230972 2012-02-04 00:54:43Z rmh Set to not build kernel modules that include sourceless code (either microcode or native code for host CPU). When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_SOURCELESS_HOST .It .Va WITHOUT_SOURCELESS_UCODE .El .It Va WITHOUT_SOURCELESS_HOST .\" from FreeBSD: head/tools/build/options/WITHOUT_SOURCELESS_HOST 230972 2012-02-04 00:54:43Z rmh Set to not build kernel modules that include sourceless native code for host CPU. .It Va WITHOUT_SOURCELESS_UCODE .\" from FreeBSD: head/tools/build/options/WITHOUT_SOURCELESS_UCODE 230972 2012-02-04 00:54:43Z rmh Set to not build kernel modules that include sourceless microcode. .It Va WITHOUT_SSP .\" from FreeBSD: head/tools/build/options/WITHOUT_SSP 180012 2008-06-25 21:33:28Z ru Set to not build world with propolice stack smashing protection. .It Va WITH_SVN .\" from FreeBSD: head/tools/build/options/WITH_SVN 252561 2013-07-03 12:36:47Z zeising Set to install .Xr svnlite 1 as .Xr svn 1 . .It Va WITHOUT_SVNLITE .\" from FreeBSD: head/tools/build/options/WITHOUT_SVNLITE 252561 2013-07-03 12:36:47Z zeising Set to not build .Xr svnlite 1 and related programs. .It Va WITHOUT_SYMVER .\" from FreeBSD: head/tools/build/options/WITHOUT_SYMVER 169649 2007-05-17 05:03:24Z deischen Set to disable symbol versioning when building shared libraries. .It Va WITHOUT_SYSCALL_COMPAT .\" from FreeBSD: head/tools/build/options/WITHOUT_SYSCALL_COMPAT 265826 2014-05-10 16:37:28Z imp Do not include some compatible syscall wrappers in libc. .It Va WITHOUT_SYSCONS .\" from FreeBSD: head/tools/build/options/WITHOUT_SYSCONS 156932 2006-03-21 07:50:50Z ru Set to not build .Xr syscons 4 support files such as keyboard maps, fonts, and screen output maps. .It Va WITHOUT_SYSINSTALL .\" from FreeBSD: head/tools/build/options/WITHOUT_SYSINSTALL 183242 2008-09-21 22:02:26Z sam Set to not build .Xr sysinstall 8 and related programs. .It Va WITHOUT_TALK .\" from FreeBSD: head/tools/build/options/WITHOUT_TALK 277676 2015-01-25 04:37:44Z ngie Set to not build or install .Xr talk 1 and .Xr talkd 8 . +.It Va WITHOUT_TCP_WRAPPERS +.\" from FreeBSD: head/tools/build/options/WITHOUT_TCP_WRAPPERS 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr tcpd 8 , +and related utilities. .It Va WITHOUT_TCSH .\" from FreeBSD: head/tools/build/options/WITHOUT_TCSH 156932 2006-03-21 07:50:50Z ru Set to not build and install .Pa /bin/csh (which is .Xr tcsh 1 ) . .It Va WITHOUT_TELNET .\" from FreeBSD: head/tools/build/options/WITHOUT_TELNET 183242 2008-09-21 22:02:26Z sam Set to not build .Xr telnet 8 and related programs. .It Va WITHOUT_TESTS .\" from FreeBSD: head/tools/build/options/WITHOUT_TESTS 268778 2014-07-16 21:40:11Z jmmv Set to not build nor install the .Fx Test Suite in .Pa /usr/tests/ . See .Xr tests 7 for more details. This also disables the build of all test-related dependencies, including ATF. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_TESTS_SUPPORT .El .It Va WITHOUT_TESTS_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_TESTS_SUPPORT 274665 2014-11-18 17:06:50Z imp Set to disables the build of all test-related dependencies, including ATF. .It Va WITHOUT_TEXTPROC .\" from FreeBSD: head/tools/build/options/WITHOUT_TEXTPROC 183242 2008-09-21 22:02:26Z sam Set to not build programs used for text processing. When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_GROFF .El +.It Va WITHOUT_TFTP +.\" from FreeBSD: head/tools/build/options/WITHOUT_TFTP 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr tftp 1 +and +.Xr tftpd 8 . +.It Va WITHOUT_TIMED +.\" from FreeBSD: head/tools/build/options/WITHOUT_TIMED 278192 2015-02-04 10:19:32Z ngie +Set to not build or install +.Xr timed 8 . .It Va WITHOUT_TOOLCHAIN .\" from FreeBSD: head/tools/build/options/WITHOUT_TOOLCHAIN 273172 2014-10-16 15:55:13Z brooks Set to not install header or programs used for program development, compilers, debuggers etc. .Bf -symbolic The option does not work for build targets. .Ef When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_BINUTILS .It .Va WITHOUT_CLANG .It .Va WITHOUT_CLANG_EXTRAS .It .Va WITHOUT_CLANG_FULL .It .Va WITHOUT_GCC .It .Va WITHOUT_GDB .It .Va WITHOUT_INCLUDES .El .It Va WITHOUT_UNBOUND .\" from FreeBSD: head/tools/build/options/WITHOUT_UNBOUND 255597 2013-09-15 14:51:23Z des Set to not build .Xr unbound 8 and related programs. .It Va WITHOUT_USB .\" from FreeBSD: head/tools/build/options/WITHOUT_USB 156932 2006-03-21 07:50:50Z ru Set to not build USB-related programs and libraries. .It Va WITHOUT_USB_GADGET_EXAMPLES .\" from FreeBSD: head/tools/build/options/WITHOUT_USB_GADGET_EXAMPLES 274665 2014-11-18 17:06:50Z imp Set to build USB gadget kernel modules. .It Va WITHOUT_UTMPX .\" from FreeBSD: head/tools/build/options/WITHOUT_UTMPX 231530 2012-02-11 20:28:42Z ed Set to not build user accounting tools such as .Xr last 1 , .Xr users 1 , .Xr who 1 , .Xr ac 8 , .Xr lastlogin 8 and .Xr utx 8 . .It Va WITHOUT_VI .\" from FreeBSD: head/tools/build/options/WITHOUT_VI 264903 2014-04-24 23:17:40Z imp Set to not build and install vi, view, ex and related programs. .It Va WITHOUT_VT .\" from FreeBSD: head/tools/build/options/WITHOUT_VT 268022 2014-06-30 00:20:12Z emaste Set to not build .Xr vt 4 support files (fonts and keymaps). .It Va WITHOUT_WARNS .\" from FreeBSD: head/tools/build/options/WITHOUT_WARNS 276559 2015-01-02 18:57:58Z bapt Set this to not add warning flags to the compiler invocations. Useful as a temporary workaround when code enters the tree which triggers warnings in environments that differ from the original developer. .It Va WITHOUT_WIRELESS .\" from FreeBSD: head/tools/build/options/WITHOUT_WIRELESS 183242 2008-09-21 22:02:26Z sam Set to not build programs used for 802.11 wireless networks; especially .Xr wpa_supplicant 8 and .Xr hostapd 8 . When set, it also enforces the following options: .Pp .Bl -item -compact .It .Va WITHOUT_WIRELESS_SUPPORT .El .It Va WITHOUT_WIRELESS_SUPPORT .\" from FreeBSD: head/tools/build/options/WITHOUT_WIRELESS_SUPPORT 183305 2008-09-23 16:11:15Z sam Set to build libraries, programs, and kernel modules without 802.11 wireless support. .It Va WITHOUT_WPA_SUPPLICANT_EAPOL .\" from FreeBSD: head/tools/build/options/WITHOUT_WPA_SUPPLICANT_EAPOL 156932 2006-03-21 07:50:50Z ru Build .Xr wpa_supplicant 8 without support for the IEEE 802.1X protocol and without support for EAP-PEAP, EAP-TLS, EAP-LEAP, and EAP-TTLS protocols (usable only via 802.1X). .It Va WITHOUT_ZFS .\" from FreeBSD: head/tools/build/options/WITHOUT_ZFS 168409 2007-04-06 02:13:30Z pjd Set to not build ZFS file system. .It Va WITHOUT_ZONEINFO .\" from FreeBSD: head/tools/build/options/WITHOUT_ZONEINFO 235342 2012-05-12 16:12:36Z gjb Set to not build the timezone database. .El .Sh FILES .Bl -tag -compact -width Pa .It Pa /etc/src.conf .It Pa /usr/share/mk/bsd.own.mk .El .Sh SEE ALSO .Xr make 1 , .Xr make.conf 5 , .Xr build 7 , .Xr ports 7 .Sh HISTORY The .Nm file appeared in .Fx 7.0 . .Sh AUTHORS This manual page was autogenerated. Index: projects/building-blocks/share/mk/src.opts.mk =================================================================== --- projects/building-blocks/share/mk/src.opts.mk (revision 278195) +++ projects/building-blocks/share/mk/src.opts.mk (revision 278196) @@ -1,408 +1,408 @@ # $FreeBSD$ # # Option file for FreeBSD /usr/src builds. # # Users define WITH_FOO and WITHOUT_FOO on the command line or in /etc/src.conf # and /etc/make.conf files. These translate in the build system to MK_FOO={yes,no} # with sensible (usually) defaults. # # Makefiles must include bsd.opts.mk after defining specific MK_FOO options that # are applicable for that Makefile (typically there are none, but sometimes there # are exceptions). Recursive makes usually add MK_FOO=no for options that they wish # to omit from that make. # # Makefiles must include bsd.mkopt.mk before they test the value of any MK_FOO # variable. # # Makefiles may also assume that this file is included by src.opts.mk should it # need variables defined there prior to the end of the Makefile where # bsd.{subdir,lib.bin}.mk is traditionally included. # # The old-style YES_FOO and NO_FOO are being phased out. No new instances of them # should be added. Old instances should be removed since they were just to # bridge the gap between FreeBSD 4 and FreeBSD 5. # # Makefiles should never test WITH_FOO or WITHOUT_FOO directly (although an # exception is made for _WITHOUT_SRCONF which turns off this mechanism # completely inside bsd.*.mk files). # .if !target(____) ____: .include # # Define MK_* variables (which are either "yes" or "no") for users # to set via WITH_*/WITHOUT_* in /etc/src.conf and override in the # make(1) environment. # These should be tested with `== "no"' or `!= "no"' in makefiles. # The NO_* variables should only be set by makefiles for variables # that haven't been converted over. # # These options are used by src the builds __DEFAULT_YES_OPTIONS = \ ACCT \ ACPI \ AMD \ APM \ AT \ ATM \ AUDIT \ AUTHPF \ AUTOFS \ BHYVE \ BINUTILS \ BINUTILS_BOOTSTRAP \ BLUETOOTH \ BOOT \ - BOOTPD \ BOOTPARAMD \ + BOOTPD \ BSD_CPIO \ BSDCONFIG \ BSDINSTALL \ BSNMP \ BZIP2 \ CALENDAR \ CAPSICUM \ CASPER \ CCD \ CDDL \ CPP \ CROSS_COMPILER \ CRYPT \ CTM \ CUSE \ CXX \ DICT \ DMAGENT \ DYNAMICROOT \ ED_CRYPTO \ EE \ ELFTOOLCHAIN_TOOLS \ EXAMPLES \ FDT \ FILE \ FINGER \ FLOPPY \ FMTREE \ FORTH \ FP_LIBC \ FREEBSD_UPDATE \ FTP \ GAMES \ GCOV \ GDB \ GNU \ GNU_GREP_COMPAT \ GPIO \ GPL_DTC \ GROFF \ HAST \ HTML \ HYPERV \ ICONV \ INET \ INET6 \ INETD \ IPFILTER \ IPFW \ ISCSI \ JAIL \ KDUMP \ KVM \ LDNS \ LDNS_UTILS \ LEGACY_CONSOLE \ LIB32 \ LIBPTHREAD \ LIBTHR \ LOCALES \ LOCATE \ LPR \ LS_COLORS \ LZMA_SUPPORT \ MAC_SUPPORT \ MAIL \ MAILWRAPPER \ MAKE \ NDIS \ NETCAT \ NETGRAPH \ NETGRAPH_SUPPORT \ NFS_SERVER \ NLS_CATALOGS \ NS_CACHING \ NTP \ OPENSSL \ PAM \ PC_SYSINSTALL \ PF \ PKGBOOTSTRAP \ PMC \ PORTSNAP \ PPP \ QUOTAS \ RADIUS_SUPPORT \ RCMDS \ RBOOTD \ RCS \ RESCUE \ ROUTED \ SENDMAIL \ SETUID_LOGIN \ SHAREDOCS \ SOURCELESS \ SOURCELESS_HOST \ SOURCELESS_UCODE \ SQLITE3 \ SVNLITE \ SYSCALL_COMPAT \ SYSCONS \ SYSINSTALL \ TALK \ TCP_WRAPPERS \ TCSH \ TELNET \ TESTS \ TEXTPROC \ TFTP \ TIMED \ UNBOUND \ USB \ UTMPX \ VI \ VT \ WIRELESS \ WPA_SUPPLICANT_EAPOL \ ZFS \ ZONEINFO __DEFAULT_NO_OPTIONS = \ BSD_GREP \ CLANG_EXTRAS \ EISA \ FMAKE \ HESIOD \ LLDB \ NAND \ OFED \ OPENLDAP \ OPENSSH_NONE_CIPHER \ SHARED_TOOLCHAIN \ SORT_THREADS \ SVN # # Default behaviour of some options depends on the architecture. Unfortunately # this means that we have to test TARGET_ARCH (the buildworld case) as well # as MACHINE_ARCH (the non-buildworld case). Normally TARGET_ARCH is not # used at all in bsd.*.mk, but we have to make an exception here if we want # to allow defaults for some things like clang to vary by target architecture. # Additional, per-target behavior should be rarely added only after much # gnashing of teeth and grinding of gears. # .if defined(TARGET_ARCH) __T=${TARGET_ARCH} .else __T=${MACHINE_ARCH} .endif .if defined(TARGET) __TT=${TARGET} .else __TT=${MACHINE} .endif .include .if !${COMPILER_FEATURES:Mc++11} # If the compiler is not C++11 capable, disable clang and use gcc instead. __DEFAULT_YES_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX __DEFAULT_NO_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_FULL CLANG_IS_CC .elif ${__T} == "amd64" || ${__T} == "i386" # On x86, clang is enabled, and will be installed as the default cc. __DEFAULT_YES_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_FULL CLANG_IS_CC __DEFAULT_NO_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX .elif ${__TT} == "arm" && ${__T:Marm*eb*} == "" # On little-endian arm, clang is enabled, and it is installed as the default # cc, but since gcc is unable to build the full clang, disable it by default. __DEFAULT_YES_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_IS_CC __DEFAULT_NO_OPTIONS+=CLANG_FULL GCC GCC_BOOTSTRAP GNUCXX .elif ${__T:Mpowerpc*} # On powerpc, clang is enabled, but gcc is installed as the default cc. __DEFAULT_YES_OPTIONS+=CLANG CLANG_FULL GCC GCC_BOOTSTRAP GNUCXX __DEFAULT_NO_OPTIONS+=CLANG_BOOTSTRAP CLANG_IS_CC .else # Everything else disables clang, and uses gcc instead. __DEFAULT_YES_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX __DEFAULT_NO_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_FULL CLANG_IS_CC .endif .include # # MK_* options that default to "yes" if the compiler is a C++11 compiler. # .for var in \ LIBCPLUSPLUS .if !defined(MK_${var}) .if ${COMPILER_FEATURES:Mc++11} .if defined(WITHOUT_${var}) MK_${var}:= no .else MK_${var}:= yes .endif .else .if defined(WITH_${var}) MK_${var}:= yes .else MK_${var}:= no .endif .endif .endif .endfor # # Force some options off if their dependencies are off. # Order is somewhat important. # .if ${MK_LIBPTHREAD} == "no" MK_LIBTHR:= no .endif .if ${MK_LDNS} == "no" MK_LDNS_UTILS:= no MK_UNBOUND:= no .endif .if ${MK_SOURCELESS} == "no" MK_SOURCELESS_HOST:= no MK_SOURCELESS_UCODE:= no .endif .if ${MK_CDDL} == "no" MK_ZFS:= no MK_CTF:= no .endif .if ${MK_CRYPT} == "no" MK_OPENSSL:= no MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_CXX} == "no" MK_CLANG:= no MK_GROFF:= no MK_GNUCXX:= no .endif .if ${MK_MAIL} == "no" MK_MAILWRAPPER:= no MK_SENDMAIL:= no MK_DMAGENT:= no .endif .if ${MK_NETGRAPH} == "no" MK_ATM:= no MK_BLUETOOTH:= no MK_NETGRAPH_SUPPORT:= no .endif .if ${MK_NETGRAPH_SUPPORT} == "no" MK_RADIUS_SUPPORT:= no .endif .if ${MK_OPENSSL} == "no" MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_PF} == "no" MK_AUTHPF:= no .endif .if ${MK_SQLITE3} == "no" MK_SVN:= no MK_SVNLITE:= no .endif .if ${MK_TEXTPROC} == "no" MK_GROFF:= no .endif .if ${MK_CROSS_COMPILER} == "no" MK_BINUTILS_BOOTSTRAP:= no MK_CLANG_BOOTSTRAP:= no MK_GCC_BOOTSTRAP:= no .endif .if ${MK_TOOLCHAIN} == "no" MK_BINUTILS:= no MK_CLANG:= no MK_GCC:= no MK_GDB:= no MK_INCLUDES:= no .endif .if ${MK_CLANG} == "no" MK_CLANG_EXTRAS:= no MK_CLANG_FULL:= no MK_CLANG_IS_CC:=no .endif # # Set defaults for the MK_*_SUPPORT variables. # # # MK_*_SUPPORT options which default to "yes" unless their corresponding # MK_* variable is set to "no". # .for var in \ BZIP2 \ GNU \ INET \ INET6 \ KERBEROS \ KVM \ NETGRAPH \ PAM \ TESTS \ WIRELESS .if defined(WITHOUT_${var}_SUPPORT) || ${MK_${var}} == "no" MK_${var}_SUPPORT:= no .else MK_${var}_SUPPORT:= yes .endif .endfor # # MK_* options whose default value depends on another option. # .for vv in \ GSSAPI/KERBEROS \ MAN_UTILS/MAN .if defined(WITH_${vv:H}) MK_${vv:H}:= yes .elif defined(WITHOUT_${vv:H}) MK_${vv:H}:= no .else MK_${vv:H}:= ${MK_${vv:T}} .endif .endfor .if !${COMPILER_FEATURES:Mc++11} MK_LLDB:= no .endif # gcc 4.8 and newer supports libc++, so suppress gnuc++ in that case. # while in theory we could build it with that, we don't want to do # that since it creates too much confusion for too little gain. .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 40800 MK_GNUCXX:=no MK_GCC:=no .endif .endif # !target(____) Index: projects/building-blocks/share =================================================================== --- projects/building-blocks/share (revision 278195) +++ projects/building-blocks/share (revision 278196) Property changes on: projects/building-blocks/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r278050-278194 Index: projects/building-blocks/sys/amd64/include/pvclock.h =================================================================== --- projects/building-blocks/sys/amd64/include/pvclock.h (nonexistent) +++ projects/building-blocks/sys/amd64/include/pvclock.h (revision 278196) @@ -0,0 +1,6 @@ +/*- + * This file is in the public domain. + */ +/* $FreeBSD$ */ + +#include Property changes on: projects/building-blocks/sys/amd64/include/pvclock.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/building-blocks/sys/arm/ti/am335x/am335x_prcm.c =================================================================== --- projects/building-blocks/sys/arm/ti/am335x/am335x_prcm.c (revision 278195) +++ projects/building-blocks/sys/arm/ti/am335x/am335x_prcm.c (revision 278196) @@ -1,798 +1,798 @@ /*- * Copyright (c) 2012 Damjan Marion * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CM_PER 0 #define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000) #define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004) #define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C) #define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014) #define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018) #define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C) #define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024) #define CM_PER_UART5_CLKCTRL (CM_PER + 0x038) #define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C) #define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044) #define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048) #define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C) #define CM_PER_UART2_CLKCTRL (CM_PER + 0x070) #define CM_PER_UART3_CLKCTRL (CM_PER + 0x074) #define CM_PER_UART4_CLKCTRL (CM_PER + 0x078) #define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C) #define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080) #define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084) #define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088) #define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC) #define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0) #define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4) #define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC) #define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC) #define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4) #define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8) #define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC) #define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0) #define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8) #define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC) #define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0) #define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4) #define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8) #define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC) #define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100) #define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C) #define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110) #define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C) #define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130) #define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144) #define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140) #define CM_WKUP 0x400 #define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000) #define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004) #define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008) #define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C) #define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C) #define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048) #define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054) #define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C) #define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098) #define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8) #define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC) #define CM_DPLL 0x500 #define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004) #define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008) #define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C) #define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010) #define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018) #define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C) #define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030) #define CM_RTC 0x800 #define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000) #define CM_RTC_CLKSTCTRL (CM_RTC + 0x004) #define PRM_PER 0xC00 #define PRM_PER_RSTCTRL (PRM_PER + 0x00) #define PRM_DEVICE_OFFSET 0xF00 #define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00) struct am335x_prcm_softc { struct resource * res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; }; static struct resource_spec am335x_prcm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static struct am335x_prcm_softc *am335x_prcm_sc = NULL; static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev); static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev); static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev); static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev); static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev); static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static void am335x_prcm_reset(void); static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev); static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev); static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev); static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev); #define AM335X_NOOP_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_noop_activate, \ .clk_deactivate = am335x_clk_noop_deactivate, \ .clk_set_source = am335x_clk_noop_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_GENERIC_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_generic_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_GPIO_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_gpio_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = NULL \ } #define AM335X_MMCHS_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = am335x_clk_generic_activate, \ .clk_deactivate = am335x_clk_generic_deactivate, \ .clk_set_source = am335x_clk_generic_set_source, \ .clk_accessible = NULL, \ .clk_get_source_freq = am335x_clk_hsmmc_get_source_freq \ } struct ti_clock_dev ti_am335x_clk_devmap[] = { /* System clocks */ { .id = SYS_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_sysclk_freq, }, /* MPU (ARM) core clocks */ { .id = MPU_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_arm_fclk_freq, }, /* CPSW Ethernet Switch core clocks */ { .id = CPSW_CLK, .clk_activate = am335x_clk_cpsw_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* Mentor USB HS controller core clocks */ { .id = MUSB0_CLK, .clk_activate = am335x_clk_musb0_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* LCD controller clocks */ { .id = LCDC_CLK, .clk_activate = am335x_clk_lcdc_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = am335x_clk_get_arm_disp_freq, }, /* UART. Uart0 clock cannot be controlled. */ AM335X_NOOP_CLOCK_DEV(UART0_CLK), AM335X_GENERIC_CLOCK_DEV(UART1_CLK), AM335X_GENERIC_CLOCK_DEV(UART2_CLK), AM335X_GENERIC_CLOCK_DEV(UART3_CLK), AM335X_GENERIC_CLOCK_DEV(UART4_CLK), AM335X_GENERIC_CLOCK_DEV(UART5_CLK), /* DMTimer */ AM335X_GENERIC_CLOCK_DEV(DMTIMER2_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER3_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER4_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER5_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER6_CLK), AM335X_GENERIC_CLOCK_DEV(DMTIMER7_CLK), /* GPIO */ AM335X_GPIO_CLOCK_DEV(GPIO0_CLK), AM335X_GPIO_CLOCK_DEV(GPIO1_CLK), AM335X_GPIO_CLOCK_DEV(GPIO2_CLK), AM335X_GPIO_CLOCK_DEV(GPIO3_CLK), /* I2C */ AM335X_GENERIC_CLOCK_DEV(I2C0_CLK), AM335X_GENERIC_CLOCK_DEV(I2C1_CLK), AM335X_GENERIC_CLOCK_DEV(I2C2_CLK), /* TSC_ADC */ AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK), /* EDMA */ AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK), AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK), /* MMCHS */ AM335X_MMCHS_CLOCK_DEV(MMC0_CLK), AM335X_MMCHS_CLOCK_DEV(MMC1_CLK), AM335X_MMCHS_CLOCK_DEV(MMC2_CLK), /* PWMSS */ AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK), AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK), AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK), /* System Mailbox clock */ AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK), /* SPINLOCK */ AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK), /* PRU-ICSS */ { .id = PRUSS_CLK, .clk_activate = am335x_clk_pruss_activate, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = NULL, }, /* RTC */ AM335X_GENERIC_CLOCK_DEV(RTC_CLK), { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } }; struct am335x_clk_details { clk_ident_t id; uint32_t clkctrl_reg; uint32_t clksel_reg; }; #define _CLK_DETAIL(i, c, s) \ { .id = (i), \ .clkctrl_reg = (c), \ .clksel_reg = (s), \ } static struct am335x_clk_details g_am335x_clk_details[] = { /* UART. UART0 clock not controllable. */ _CLK_DETAIL(UART0_CLK, 0, 0), _CLK_DETAIL(UART1_CLK, CM_PER_UART1_CLKCTRL, 0), _CLK_DETAIL(UART2_CLK, CM_PER_UART2_CLKCTRL, 0), _CLK_DETAIL(UART3_CLK, CM_PER_UART3_CLKCTRL, 0), _CLK_DETAIL(UART4_CLK, CM_PER_UART4_CLKCTRL, 0), _CLK_DETAIL(UART5_CLK, CM_PER_UART5_CLKCTRL, 0), /* DMTimer modules */ _CLK_DETAIL(DMTIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK), _CLK_DETAIL(DMTIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK), _CLK_DETAIL(DMTIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK), _CLK_DETAIL(DMTIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK), _CLK_DETAIL(DMTIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK), _CLK_DETAIL(DMTIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK), /* GPIO modules */ _CLK_DETAIL(GPIO0_CLK, CM_WKUP_GPIO0_CLKCTRL, 0), _CLK_DETAIL(GPIO1_CLK, CM_PER_GPIO1_CLKCTRL, 0), _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO2_CLKCTRL, 0), _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO3_CLKCTRL, 0), /* I2C modules */ _CLK_DETAIL(I2C0_CLK, CM_WKUP_I2C0_CLKCTRL, 0), _CLK_DETAIL(I2C1_CLK, CM_PER_I2C1_CLKCTRL, 0), _CLK_DETAIL(I2C2_CLK, CM_PER_I2C2_CLKCTRL, 0), /* TSC_ADC module */ _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0), /* EDMA modules */ _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0), _CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0), /* MMCHS modules*/ _CLK_DETAIL(MMC0_CLK, CM_PER_MMC0_CLKCTRL, 0), _CLK_DETAIL(MMC1_CLK, CM_PER_MMC1_CLKCTRL, 0), _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0), /* PWMSS modules */ _CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0), _CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0), _CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0), _CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0), _CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0), /* RTC module */ _CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0), { INVALID_CLK_IDENT, 0}, }; /* Read/Write macros */ #define prcm_read_4(reg) \ bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg) #define prcm_write_4(reg, val) \ bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val) void am335x_prcm_setup_dmtimer(int); static int am335x_prcm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "am335x,prcm")) { device_set_desc(dev, "AM335x Power and Clock Management"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int am335x_prcm_attach(device_t dev) { struct am335x_prcm_softc *sc = device_get_softc(dev); unsigned int sysclk, fclk; if (am335x_prcm_sc) return (ENXIO); if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); am335x_prcm_sc = sc; ti_cpu_reset = am335x_prcm_reset; am335x_clk_get_sysclk_freq(NULL, &sysclk); am335x_clk_get_arm_fclk_freq(NULL, &fclk); device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n", sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000); return (0); } static device_method_t am335x_prcm_methods[] = { DEVMETHOD(device_probe, am335x_prcm_probe), DEVMETHOD(device_attach, am335x_prcm_attach), { 0, 0 } }; static driver_t am335x_prcm_driver = { "am335x_prcm", am335x_prcm_methods, sizeof(struct am335x_prcm_softc), }; static devclass_t am335x_prcm_devclass; DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver, am335x_prcm_devclass, 0, 0); MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1); static struct am335x_clk_details* am335x_clk_details(clk_ident_t id) { struct am335x_clk_details *walker; for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { if (id == walker->id) return (walker); } return NULL; } static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev) { return (0); } static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ prcm_write_4(clk_details->clkctrl_reg, 2); while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2) DELAY(10); return (0); } static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */ /* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */ prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18)); - while ((prcm_read_4(clk_details->clkctrl_reg) & + while ((prcm_read_4(clk_details->clkctrl_reg) & (3 | (1 << 18) )) != (2 | (1 << 18))) DELAY(10); return (0); } static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev) { return(0); } static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */ prcm_write_4(clk_details->clkctrl_reg, 0); while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0) DELAY(10); return (0); } static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { return (0); } static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct am335x_prcm_softc *sc = am335x_prcm_sc; struct am335x_clk_details* clk_details; uint32_t reg; if (sc == NULL) return ENXIO; clk_details = am335x_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); switch (clksrc) { case EXT_CLK: reg = 0; /* SEL2: TCLKIN clock */ break; case SYSCLK_CLK: reg = 1; /* SEL1: CLK_M_OSC clock */ break; case F32KHZ_CLK: reg = 2; /* SEL3: CLK_32KHZ clock */ break; default: return (ENXIO); } prcm_write_4(clk_details->clksel_reg, reg); while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg) DELAY(10); return (0); } static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { *freq = 96000000; return (0); } static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t ctrl_status; /* Read the input clock freq from the control module */ /* control_status reg (0x40) */ if (ti_scm_reg_read_4(0x40, &ctrl_status)) return ENXIO; switch ((ctrl_status>>22) & 0x3) { case 0x0: /* 19.2Mhz */ *freq = 19200000; break; case 0x1: /* 24Mhz */ *freq = 24000000; break; case 0x2: /* 25Mhz */ *freq = 25000000; break; case 0x3: /* 26Mhz */ *freq = 26000000; break; } return (0); } #define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1) #define DPLL_DIV(reg) ((reg & 0x7f)+1) #define DPLL_MULT(reg) ((reg>>8) & 0x7FF) static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t reg; uint32_t sysclk; reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU); /*Check if we are running in bypass */ if (DPLL_BYP_CLKSEL(reg)) return ENXIO; am335x_clk_get_sysclk_freq(NULL, &sysclk); *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); return(0); } static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t reg; uint32_t sysclk; reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP); /*Check if we are running in bypass */ if (DPLL_BYP_CLKSEL(reg)) return ENXIO; am335x_clk_get_sysclk_freq(NULL, &sysclk); *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg)); return(0); } static void am335x_prcm_reset(void) { prcm_write_4(PRM_RSTCTRL, (1<<1)); } static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return ENXIO; /* set MODULENAME to ENABLE */ prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16)); /*set CLKTRCTRL to SW_WKUP(2) */ prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2); /* wait for 125 MHz OCP clock to become active */ while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0); return(0); } static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return ENXIO; /* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */ /* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/ prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300); /*set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_USB0_CLKCTRL, 2); /* wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2) DELAY(10); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16)) DELAY(10); return(0); } static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return (ENXIO); /* Bypass mode */ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4); /* Make sure it's in bypass mode */ - while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) + while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 8))) DELAY(10); - /* + /* * For now set frequency to 99*SYSFREQ/8 which is twice as * HDMI 1080p pixel clock (minimum LCDC freq divisor is 2) */ prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (99 << 8) | 8); /* Locked mode */ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7); int timeout = 10000; - while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) + while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP) & (1 << 0))) && timeout--) DELAY(10); /*set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_LCDC_CLKCTRL, 2); /* wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2) DELAY(10); /* wait for IDLEST to become Func(0) */ while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16)) DELAY(10); return (0); } static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev) { struct am335x_prcm_softc *sc = am335x_prcm_sc; if (sc == NULL) return (ENXIO); /* Set MODULEMODE to ENABLE(2) */ prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2); /* Wait for MODULEMODE to become ENABLE(2) */ while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2) DELAY(10); /* Set CLKTRCTRL to SW_WKUP(2) */ prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2); /* Wait for the 200 MHz OCP clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0) DELAY(10); /* Wait for the 200 MHz IEP clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0) DELAY(10); /* Wait for the 192 MHz UART clock to become active */ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0) DELAY(10); - /* Select DISP DPLL as OCP clock */ - prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 1); - while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 1) + /* Select L3F as OCP clock */ + prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0); + while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0) DELAY(10); /* Clear the RESET bit */ prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2); return (0); } Index: projects/building-blocks/sys/cam/cam.h =================================================================== --- projects/building-blocks/sys/cam/cam.h (revision 278195) +++ projects/building-blocks/sys/cam/cam.h (revision 278196) @@ -1,400 +1,401 @@ /*- * Data structures and definitions for the CAM system. * * Copyright (c) 1997 Justin T. Gibbs. * 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, immediately at the beginning of the file. * 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 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 _CAM_CAM_H #define _CAM_CAM_H 1 #ifdef _KERNEL #include #endif #include typedef u_int path_id_t; typedef u_int target_id_t; typedef u_int64_t lun_id_t; #define CAM_XPT_PATH_ID ((path_id_t)~0) #define CAM_BUS_WILDCARD ((path_id_t)~0) #define CAM_TARGET_WILDCARD ((target_id_t)~0) #define CAM_LUN_WILDCARD (~(u_int)0) #define CAM_EXTLUN_BYTE_SWIZZLE(lun) ( \ ((((u_int64_t)lun) & 0xffff000000000000L) >> 48) | \ ((((u_int64_t)lun) & 0x0000ffff00000000L) >> 16) | \ ((((u_int64_t)lun) & 0x00000000ffff0000L) << 16) | \ ((((u_int64_t)lun) & 0x000000000000ffffL) << 48)) /* * Maximum length for a CAM CDB. */ #define CAM_MAX_CDBLEN 16 /* * Definition of a CAM peripheral driver entry. Peripheral drivers instantiate * one of these for each device they wish to communicate with and pass it into * the xpt layer when they wish to schedule work on that device via the * xpt_schedule API. */ struct cam_periph; /* * Priority information for a CAM structure. */ typedef enum { CAM_RL_HOST, CAM_RL_BUS, CAM_RL_XPT, CAM_RL_DEV, CAM_RL_NORMAL, CAM_RL_VALUES } cam_rl; /* * The generation number is incremented everytime a new entry is entered into * the queue giving round robin per priority level scheduling. */ typedef struct { u_int32_t priority; #define CAM_PRIORITY_HOST ((CAM_RL_HOST << 8) + 0x80) #define CAM_PRIORITY_BUS ((CAM_RL_BUS << 8) + 0x80) #define CAM_PRIORITY_XPT ((CAM_RL_XPT << 8) + 0x80) #define CAM_PRIORITY_DEV ((CAM_RL_DEV << 8) + 0x80) #define CAM_PRIORITY_OOB (CAM_RL_DEV << 8) #define CAM_PRIORITY_NORMAL ((CAM_RL_NORMAL << 8) + 0x80) #define CAM_PRIORITY_NONE (u_int32_t)-1 u_int32_t generation; int index; #define CAM_UNQUEUED_INDEX -1 #define CAM_ACTIVE_INDEX -2 #define CAM_DONEQ_INDEX -3 #define CAM_EXTRAQ_INDEX INT_MAX } cam_pinfo; /* * Macro to compare two generation numbers. It is used like this: * * if (GENERATIONCMP(a, >=, b)) * ...; * * GERERATIONCMP uses modular arithmetic to guard against wraps * wraps in the generation number. */ #define GENERATIONCMP(x, op, y) ((int32_t)((x) - (y)) op 0) /* CAM flags XXX Move to cam_periph.h ??? */ typedef enum { CAM_FLAG_NONE = 0x00, CAM_EXPECT_INQ_CHANGE = 0x01, CAM_RETRY_SELTO = 0x02 /* Retry Selection Timeouts */ } cam_flags; enum { SF_RETRY_UA = 0x01, /* Retry UNIT ATTENTION conditions. */ SF_NO_PRINT = 0x02, /* Never print error status. */ SF_QUIET_IR = 0x04, /* Be quiet about Illegal Request reponses */ SF_PRINT_ALWAYS = 0x08, /* Always print error status. */ SF_NO_RECOVERY = 0x10, /* Don't do active error recovery. */ - SF_NO_RETRY = 0x20 /* Don't do any retries. */ + SF_NO_RETRY = 0x20, /* Don't do any retries. */ + SF_RETRY_BUSY = 0x40 /* Retry BUSY status. */ }; /* CAM Status field values */ typedef enum { /* CCB request is in progress */ CAM_REQ_INPROG = 0x00, /* CCB request completed without error */ CAM_REQ_CMP = 0x01, /* CCB request aborted by the host */ CAM_REQ_ABORTED = 0x02, /* Unable to abort CCB request */ CAM_UA_ABORT = 0x03, /* CCB request completed with an error */ CAM_REQ_CMP_ERR = 0x04, /* CAM subsystem is busy */ CAM_BUSY = 0x05, /* CCB request was invalid */ CAM_REQ_INVALID = 0x06, /* Supplied Path ID is invalid */ CAM_PATH_INVALID = 0x07, /* SCSI Device Not Installed/there */ CAM_DEV_NOT_THERE = 0x08, /* Unable to terminate I/O CCB request */ CAM_UA_TERMIO = 0x09, /* Target Selection Timeout */ CAM_SEL_TIMEOUT = 0x0a, /* Command timeout */ CAM_CMD_TIMEOUT = 0x0b, /* SCSI error, look at error code in CCB */ CAM_SCSI_STATUS_ERROR = 0x0c, /* Message Reject Received */ CAM_MSG_REJECT_REC = 0x0d, /* SCSI Bus Reset Sent/Received */ CAM_SCSI_BUS_RESET = 0x0e, /* Uncorrectable parity error occurred */ CAM_UNCOR_PARITY = 0x0f, /* Autosense: request sense cmd fail */ CAM_AUTOSENSE_FAIL = 0x10, /* No HBA Detected error */ CAM_NO_HBA = 0x11, /* Data Overrun error */ CAM_DATA_RUN_ERR = 0x12, /* Unexpected Bus Free */ CAM_UNEXP_BUSFREE = 0x13, /* Target Bus Phase Sequence Failure */ CAM_SEQUENCE_FAIL = 0x14, /* CCB length supplied is inadequate */ CAM_CCB_LEN_ERR = 0x15, /* Unable to provide requested capability*/ CAM_PROVIDE_FAIL = 0x16, /* A SCSI BDR msg was sent to target */ CAM_BDR_SENT = 0x17, /* CCB request terminated by the host */ CAM_REQ_TERMIO = 0x18, /* Unrecoverable Host Bus Adapter Error */ CAM_UNREC_HBA_ERROR = 0x19, /* Request was too large for this host */ CAM_REQ_TOO_BIG = 0x1a, /* * This request should be requeued to preserve * transaction ordering. This typically occurs * when the SIM recognizes an error that should * freeze the queue and must place additional * requests for the target at the sim level * back into the XPT queue. */ CAM_REQUEUE_REQ = 0x1b, /* ATA error, look at error code in CCB */ CAM_ATA_STATUS_ERROR = 0x1c, /* Initiator/Target Nexus lost. */ CAM_SCSI_IT_NEXUS_LOST = 0x1d, /* SMP error, look at error code in CCB */ CAM_SMP_STATUS_ERROR = 0x1e, /* * Command completed without error but exceeded the soft * timeout threshold. */ CAM_REQ_SOFTTIMEOUT = 0x1f, /* * 0x20 - 0x32 are unassigned */ /* Initiator Detected Error */ CAM_IDE = 0x33, /* Resource Unavailable */ CAM_RESRC_UNAVAIL = 0x34, /* Unacknowledged Event by Host */ CAM_UNACKED_EVENT = 0x35, /* Message Received in Host Target Mode */ CAM_MESSAGE_RECV = 0x36, /* Invalid CDB received in Host Target Mode */ CAM_INVALID_CDB = 0x37, /* Lun supplied is invalid */ CAM_LUN_INVALID = 0x38, /* Target ID supplied is invalid */ CAM_TID_INVALID = 0x39, /* The requested function is not available */ CAM_FUNC_NOTAVAIL = 0x3a, /* Nexus is not established */ CAM_NO_NEXUS = 0x3b, /* The initiator ID is invalid */ CAM_IID_INVALID = 0x3c, /* The SCSI CDB has been received */ CAM_CDB_RECVD = 0x3d, /* The LUN is already enabled for target mode */ CAM_LUN_ALRDY_ENA = 0x3e, /* SCSI Bus Busy */ CAM_SCSI_BUSY = 0x3f, /* * Flags */ /* The DEV queue is frozen w/this err */ CAM_DEV_QFRZN = 0x40, /* Autosense data valid for target */ CAM_AUTOSNS_VALID = 0x80, /* SIM ready to take more commands */ CAM_RELEASE_SIMQ = 0x100, /* SIM has this command in it's queue */ CAM_SIM_QUEUED = 0x200, /* Quality of service data is valid */ CAM_QOS_VALID = 0x400, /* Mask bits for just the status # */ CAM_STATUS_MASK = 0x3F, /* * Target Specific Adjunct Status */ /* sent sense with status */ CAM_SENT_SENSE = 0x40000000 } cam_status; typedef enum { CAM_ESF_NONE = 0x00, CAM_ESF_COMMAND = 0x01, CAM_ESF_CAM_STATUS = 0x02, CAM_ESF_PROTO_STATUS = 0x04, CAM_ESF_ALL = 0xff } cam_error_string_flags; typedef enum { CAM_EPF_NONE = 0x00, CAM_EPF_MINIMAL = 0x01, CAM_EPF_NORMAL = 0x02, CAM_EPF_ALL = 0x03, CAM_EPF_LEVEL_MASK = 0x0f /* All bits above bit 3 are protocol-specific */ } cam_error_proto_flags; typedef enum { CAM_ESF_PRINT_NONE = 0x00, CAM_ESF_PRINT_STATUS = 0x10, CAM_ESF_PRINT_SENSE = 0x20 } cam_error_scsi_flags; typedef enum { CAM_ESMF_PRINT_NONE = 0x00, CAM_ESMF_PRINT_STATUS = 0x10, CAM_ESMF_PRINT_FULL_CMD = 0x20, } cam_error_smp_flags; typedef enum { CAM_EAF_PRINT_NONE = 0x00, CAM_EAF_PRINT_STATUS = 0x10, CAM_EAF_PRINT_RESULT = 0x20 } cam_error_ata_flags; struct cam_status_entry { cam_status status_code; const char *status_text; }; extern const struct cam_status_entry cam_status_table[]; extern const int num_cam_status_entries; #ifdef _KERNEL extern int cam_sort_io_queues; #endif union ccb; #ifdef SYSCTL_DECL /* from sysctl.h */ SYSCTL_DECL(_kern_cam); #endif __BEGIN_DECLS typedef int (cam_quirkmatch_t)(caddr_t, caddr_t); caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, int entry_size, cam_quirkmatch_t *comp_func); void cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen); int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, int str_len); const struct cam_status_entry* cam_fetch_status_entry(cam_status status); #ifdef _KERNEL char * cam_error_string(union ccb *ccb, char *str, int str_len, cam_error_string_flags flags, cam_error_proto_flags proto_flags); void cam_error_print(union ccb *ccb, cam_error_string_flags flags, cam_error_proto_flags proto_flags); #else /* _KERNEL */ struct cam_device; char * cam_error_string(struct cam_device *device, union ccb *ccb, char *str, int str_len, cam_error_string_flags flags, cam_error_proto_flags proto_flags); void cam_error_print(struct cam_device *device, union ccb *ccb, cam_error_string_flags flags, cam_error_proto_flags proto_flags, FILE *ofile); #endif /* _KERNEL */ __END_DECLS #ifdef _KERNEL static __inline void cam_init_pinfo(cam_pinfo *pinfo); static __inline void cam_init_pinfo(cam_pinfo *pinfo) { pinfo->priority = CAM_PRIORITY_NONE; pinfo->index = CAM_UNQUEUED_INDEX; } #endif #endif /* _CAM_CAM_H */ Index: projects/building-blocks/sys/cam/cam_periph.c =================================================================== --- projects/building-blocks/sys/cam/cam_periph.c (revision 278195) +++ projects/building-blocks/sys/cam/cam_periph.c (revision 278196) @@ -1,1806 +1,1806 @@ /*- * Common functions for CAM "type" (peripheral) drivers. * * Copyright (c) 1997, 1998 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry. * 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, immediately at the beginning of the file. * 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static u_int camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, path_id_t pathid, target_id_t target, lun_id_t lun); static u_int camperiphunit(struct periph_driver *p_drv, path_id_t pathid, target_id_t target, lun_id_t lun); static void camperiphdone(struct cam_periph *periph, union ccb *done_ccb); static void camperiphfree(struct cam_periph *periph); static int camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb, cam_flags camflags, u_int32_t sense_flags, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout, u_int32_t *action, const char **action_string); static int camperiphscsisenseerror(union ccb *ccb, union ccb **orig_ccb, cam_flags camflags, u_int32_t sense_flags, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout, u_int32_t *action, const char **action_string); static int nperiph_drivers; static int initialized = 0; struct periph_driver **periph_drivers; static MALLOC_DEFINE(M_CAMPERIPH, "CAM periph", "CAM peripheral buffers"); static int periph_selto_delay = 1000; TUNABLE_INT("kern.cam.periph_selto_delay", &periph_selto_delay); static int periph_noresrc_delay = 500; TUNABLE_INT("kern.cam.periph_noresrc_delay", &periph_noresrc_delay); static int periph_busy_delay = 500; TUNABLE_INT("kern.cam.periph_busy_delay", &periph_busy_delay); void periphdriver_register(void *data) { struct periph_driver *drv = (struct periph_driver *)data; struct periph_driver **newdrivers, **old; int ndrivers; ndrivers = nperiph_drivers + 2; newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH, M_WAITOK); if (periph_drivers) bcopy(periph_drivers, newdrivers, sizeof(*newdrivers) * nperiph_drivers); newdrivers[nperiph_drivers] = drv; newdrivers[nperiph_drivers + 1] = NULL; old = periph_drivers; periph_drivers = newdrivers; if (old) free(old, M_CAMPERIPH); nperiph_drivers++; /* If driver marked as early or it is late now, initialize it. */ if (((drv->flags & CAM_PERIPH_DRV_EARLY) != 0 && initialized > 0) || initialized > 1) (*drv->init)(); } void periphdriver_init(int level) { int i, early; initialized = max(initialized, level); for (i = 0; periph_drivers[i] != NULL; i++) { early = (periph_drivers[i]->flags & CAM_PERIPH_DRV_EARLY) ? 1 : 2; if (early == initialized) (*periph_drivers[i]->init)(); } } cam_status cam_periph_alloc(periph_ctor_t *periph_ctor, periph_oninv_t *periph_oninvalidate, periph_dtor_t *periph_dtor, periph_start_t *periph_start, char *name, cam_periph_type type, struct cam_path *path, ac_callback_t *ac_callback, ac_code code, void *arg) { struct periph_driver **p_drv; struct cam_sim *sim; struct cam_periph *periph; struct cam_periph *cur_periph; path_id_t path_id; target_id_t target_id; lun_id_t lun_id; cam_status status; u_int init_level; init_level = 0; /* * Handle Hot-Plug scenarios. If there is already a peripheral * of our type assigned to this path, we are likely waiting for * final close on an old, invalidated, peripheral. If this is * the case, queue up a deferred call to the peripheral's async * handler. If it looks like a mistaken re-allocation, complain. */ if ((periph = cam_periph_find(path, name)) != NULL) { if ((periph->flags & CAM_PERIPH_INVALID) != 0 && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; periph->deferred_callback = ac_callback; periph->deferred_ac = code; return (CAM_REQ_INPROG); } else { printf("cam_periph_alloc: attempt to re-allocate " "valid device %s%d rejected flags %#x " "refcount %d\n", periph->periph_name, periph->unit_number, periph->flags, periph->refcount); } return (CAM_REQ_INVALID); } periph = (struct cam_periph *)malloc(sizeof(*periph), M_CAMPERIPH, M_NOWAIT|M_ZERO); if (periph == NULL) return (CAM_RESRC_UNAVAIL); init_level++; sim = xpt_path_sim(path); path_id = xpt_path_path_id(path); target_id = xpt_path_target_id(path); lun_id = xpt_path_lun_id(path); periph->periph_start = periph_start; periph->periph_dtor = periph_dtor; periph->periph_oninval = periph_oninvalidate; periph->type = type; periph->periph_name = name; periph->scheduled_priority = CAM_PRIORITY_NONE; periph->immediate_priority = CAM_PRIORITY_NONE; periph->refcount = 1; /* Dropped by invalidation. */ periph->sim = sim; SLIST_INIT(&periph->ccb_list); status = xpt_create_path(&path, periph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) goto failure; periph->path = path; xpt_lock_buses(); for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (strcmp((*p_drv)->driver_name, name) == 0) break; } if (*p_drv == NULL) { printf("cam_periph_alloc: invalid periph name '%s'\n", name); xpt_unlock_buses(); xpt_free_path(periph->path); free(periph, M_CAMPERIPH); return (CAM_REQ_INVALID); } periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); cur_periph = TAILQ_FIRST(&(*p_drv)->units); while (cur_periph != NULL && cur_periph->unit_number < periph->unit_number) cur_periph = TAILQ_NEXT(cur_periph, unit_links); if (cur_periph != NULL) { KASSERT(cur_periph->unit_number != periph->unit_number, ("duplicate units on periph list")); TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); } else { TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); (*p_drv)->generation++; } xpt_unlock_buses(); init_level++; status = xpt_add_periph(periph); if (status != CAM_REQ_CMP) goto failure; init_level++; CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph created\n")); status = periph_ctor(periph, arg); if (status == CAM_REQ_CMP) init_level++; failure: switch (init_level) { case 4: /* Initialized successfully */ break; case 3: CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); xpt_remove_periph(periph); /* FALLTHROUGH */ case 2: xpt_lock_buses(); TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); xpt_unlock_buses(); xpt_free_path(periph->path); /* FALLTHROUGH */ case 1: free(periph, M_CAMPERIPH); /* FALLTHROUGH */ case 0: /* No cleanup to perform. */ break; default: panic("%s: Unknown init level", __func__); } return(status); } /* * Find a peripheral structure with the specified path, target, lun, * and (optionally) type. If the name is NULL, this function will return * the first peripheral driver that matches the specified path. */ struct cam_periph * cam_periph_find(struct cam_path *path, char *name) { struct periph_driver **p_drv; struct cam_periph *periph; xpt_lock_buses(); for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) continue; TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { if (xpt_path_comp(periph->path, path) == 0) { xpt_unlock_buses(); cam_periph_assert(periph, MA_OWNED); return(periph); } } if (name != NULL) { xpt_unlock_buses(); return(NULL); } } xpt_unlock_buses(); return(NULL); } /* * Find peripheral driver instances attached to the specified path. */ int cam_periph_list(struct cam_path *path, struct sbuf *sb) { struct sbuf local_sb; struct periph_driver **p_drv; struct cam_periph *periph; int count; int sbuf_alloc_len; sbuf_alloc_len = 16; retry: sbuf_new(&local_sb, NULL, sbuf_alloc_len, SBUF_FIXEDLEN); count = 0; xpt_lock_buses(); for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { if (xpt_path_comp(periph->path, path) != 0) continue; if (sbuf_len(&local_sb) != 0) sbuf_cat(&local_sb, ","); sbuf_printf(&local_sb, "%s%d", periph->periph_name, periph->unit_number); if (sbuf_error(&local_sb) == ENOMEM) { sbuf_alloc_len *= 2; xpt_unlock_buses(); sbuf_delete(&local_sb); goto retry; } count++; } } xpt_unlock_buses(); sbuf_finish(&local_sb); sbuf_cpy(sb, sbuf_data(&local_sb)); sbuf_delete(&local_sb); return (count); } cam_status cam_periph_acquire(struct cam_periph *periph) { cam_status status; status = CAM_REQ_CMP_ERR; if (periph == NULL) return (status); xpt_lock_buses(); if ((periph->flags & CAM_PERIPH_INVALID) == 0) { periph->refcount++; status = CAM_REQ_CMP; } xpt_unlock_buses(); return (status); } void cam_periph_doacquire(struct cam_periph *periph) { xpt_lock_buses(); KASSERT(periph->refcount >= 1, ("cam_periph_doacquire() with refcount == %d", periph->refcount)); periph->refcount++; xpt_unlock_buses(); } void cam_periph_release_locked_buses(struct cam_periph *periph) { cam_periph_assert(periph, MA_OWNED); KASSERT(periph->refcount >= 1, ("periph->refcount >= 1")); if (--periph->refcount == 0) camperiphfree(periph); } void cam_periph_release_locked(struct cam_periph *periph) { if (periph == NULL) return; xpt_lock_buses(); cam_periph_release_locked_buses(periph); xpt_unlock_buses(); } void cam_periph_release(struct cam_periph *periph) { struct mtx *mtx; if (periph == NULL) return; cam_periph_assert(periph, MA_NOTOWNED); mtx = cam_periph_mtx(periph); mtx_lock(mtx); cam_periph_release_locked(periph); mtx_unlock(mtx); } int cam_periph_hold(struct cam_periph *periph, int priority) { int error; /* * Increment the reference count on the peripheral * while we wait for our lock attempt to succeed * to ensure the peripheral doesn't disappear out * from user us while we sleep. */ if (cam_periph_acquire(periph) != CAM_REQ_CMP) return (ENXIO); cam_periph_assert(periph, MA_OWNED); while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { periph->flags |= CAM_PERIPH_LOCK_WANTED; if ((error = cam_periph_sleep(periph, periph, priority, "caplck", 0)) != 0) { cam_periph_release_locked(periph); return (error); } if (periph->flags & CAM_PERIPH_INVALID) { cam_periph_release_locked(periph); return (ENXIO); } } periph->flags |= CAM_PERIPH_LOCKED; return (0); } void cam_periph_unhold(struct cam_periph *periph) { cam_periph_assert(periph, MA_OWNED); periph->flags &= ~CAM_PERIPH_LOCKED; if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { periph->flags &= ~CAM_PERIPH_LOCK_WANTED; wakeup(periph); } cam_periph_release_locked(periph); } /* * Look for the next unit number that is not currently in use for this * peripheral type starting at "newunit". Also exclude unit numbers that * are reserved by for future "hardwiring" unless we already know that this * is a potential wired device. Only assume that the device is "wired" the * first time through the loop since after that we'll be looking at unit * numbers that did not match a wiring entry. */ static u_int camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, path_id_t pathid, target_id_t target, lun_id_t lun) { struct cam_periph *periph; char *periph_name; int i, val, dunit, r; const char *dname, *strval; periph_name = p_drv->driver_name; for (;;newunit++) { for (periph = TAILQ_FIRST(&p_drv->units); periph != NULL && periph->unit_number != newunit; periph = TAILQ_NEXT(periph, unit_links)) ; if (periph != NULL && periph->unit_number == newunit) { if (wired != 0) { xpt_print(periph->path, "Duplicate Wired " "Device entry!\n"); xpt_print(periph->path, "Second device (%s " "device at scbus%d target %d lun %d) will " "not be wired\n", periph_name, pathid, target, lun); wired = 0; } continue; } if (wired) break; /* * Don't match entries like "da 4" as a wired down * device, but do match entries like "da 4 target 5" * or even "da 4 scbus 1". */ i = 0; dname = periph_name; for (;;) { r = resource_find_dev(&i, dname, &dunit, NULL, NULL); if (r != 0) break; /* if no "target" and no specific scbus, skip */ if (resource_int_value(dname, dunit, "target", &val) && (resource_string_value(dname, dunit, "at",&strval)|| strcmp(strval, "scbus") == 0)) continue; if (newunit == dunit) break; } if (r != 0) break; } return (newunit); } static u_int camperiphunit(struct periph_driver *p_drv, path_id_t pathid, target_id_t target, lun_id_t lun) { u_int unit; int wired, i, val, dunit; const char *dname, *strval; char pathbuf[32], *periph_name; periph_name = p_drv->driver_name; snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); unit = 0; i = 0; dname = periph_name; for (wired = 0; resource_find_dev(&i, dname, &dunit, NULL, NULL) == 0; wired = 0) { if (resource_string_value(dname, dunit, "at", &strval) == 0) { if (strcmp(strval, pathbuf) != 0) continue; wired++; } if (resource_int_value(dname, dunit, "target", &val) == 0) { if (val != target) continue; wired++; } if (resource_int_value(dname, dunit, "lun", &val) == 0) { if (val != lun) continue; wired++; } if (wired != 0) { unit = dunit; break; } } /* * Either start from 0 looking for the next unit or from * the unit number given in the resource config. This way, * if we have wildcard matches, we don't return the same * unit number twice. */ unit = camperiphnextunit(p_drv, unit, wired, pathid, target, lun); return (unit); } void cam_periph_invalidate(struct cam_periph *periph) { cam_periph_assert(periph, MA_OWNED); /* * We only call this routine the first time a peripheral is * invalidated. */ if ((periph->flags & CAM_PERIPH_INVALID) != 0) return; CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph invalidated\n")); if ((periph->flags & CAM_PERIPH_ANNOUNCED) && !rebooting) xpt_denounce_periph(periph); periph->flags |= CAM_PERIPH_INVALID; periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; if (periph->periph_oninval != NULL) periph->periph_oninval(periph); cam_periph_release_locked(periph); } static void camperiphfree(struct cam_periph *periph) { struct periph_driver **p_drv; cam_periph_assert(periph, MA_OWNED); KASSERT(periph->periph_allocating == 0, ("%s%d: freed while allocating", periph->periph_name, periph->unit_number)); for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) break; } if (*p_drv == NULL) { printf("camperiphfree: attempt to free non-existant periph\n"); return; } /* * We need to set this flag before dropping the topology lock, to * let anyone who is traversing the list that this peripheral is * about to be freed, and there will be no more reference count * checks. */ periph->flags |= CAM_PERIPH_FREE; /* * The peripheral destructor semantics dictate calling with only the * SIM mutex held. Since it might sleep, it should not be called * with the topology lock held. */ xpt_unlock_buses(); /* * We need to call the peripheral destructor prior to removing the * peripheral from the list. Otherwise, we risk running into a * scenario where the peripheral unit number may get reused * (because it has been removed from the list), but some resources * used by the peripheral are still hanging around. In particular, * the devfs nodes used by some peripherals like the pass(4) driver * aren't fully cleaned up until the destructor is run. If the * unit number is reused before the devfs instance is fully gone, * devfs will panic. */ if (periph->periph_dtor != NULL) periph->periph_dtor(periph); /* * The peripheral list is protected by the topology lock. */ xpt_lock_buses(); TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); (*p_drv)->generation++; xpt_remove_periph(periph); xpt_unlock_buses(); if ((periph->flags & CAM_PERIPH_ANNOUNCED) && !rebooting) xpt_print(periph->path, "Periph destroyed\n"); else CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { union ccb ccb; void *arg; switch (periph->deferred_ac) { case AC_FOUND_DEVICE: ccb.ccb_h.func_code = XPT_GDEV_TYPE; xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); xpt_action(&ccb); arg = &ccb; break; case AC_PATH_REGISTERED: ccb.ccb_h.func_code = XPT_PATH_INQ; xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); xpt_action(&ccb); arg = &ccb; break; default: arg = NULL; break; } periph->deferred_callback(NULL, periph->deferred_ac, periph->path, arg); } xpt_free_path(periph->path); free(periph, M_CAMPERIPH); xpt_lock_buses(); } /* * Map user virtual pointers into kernel virtual address space, so we can * access the memory. This is now a generic function that centralizes most * of the sanity checks on the data flags, if any. * This also only works for up to MAXPHYS memory. Since we use * buffers to map stuff in and out, we're limited to the buffer size. */ int cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) { int numbufs, i, j; int flags[CAM_PERIPH_MAXMAPS]; u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; u_int32_t lengths[CAM_PERIPH_MAXMAPS]; u_int32_t dirs[CAM_PERIPH_MAXMAPS]; /* Some controllers may not be able to handle more data. */ size_t maxmap = DFLTPHYS; switch(ccb->ccb_h.func_code) { case XPT_DEV_MATCH: if (ccb->cdm.match_buf_len == 0) { printf("cam_periph_mapmem: invalid match buffer " "length 0\n"); return(EINVAL); } if (ccb->cdm.pattern_buf_len > 0) { data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; lengths[0] = ccb->cdm.pattern_buf_len; dirs[0] = CAM_DIR_OUT; data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; lengths[1] = ccb->cdm.match_buf_len; dirs[1] = CAM_DIR_IN; numbufs = 2; } else { data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; lengths[0] = ccb->cdm.match_buf_len; dirs[0] = CAM_DIR_IN; numbufs = 1; } /* * This request will not go to the hardware, no reason * to be so strict. vmapbuf() is able to map up to MAXPHYS. */ maxmap = MAXPHYS; break; case XPT_SCSI_IO: case XPT_CONT_TARGET_IO: if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) return(0); if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) return (EINVAL); data_ptrs[0] = &ccb->csio.data_ptr; lengths[0] = ccb->csio.dxfer_len; dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; numbufs = 1; break; case XPT_ATA_IO: if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) return(0); if ((ccb->ccb_h.flags & CAM_DATA_MASK) != CAM_DATA_VADDR) return (EINVAL); data_ptrs[0] = &ccb->ataio.data_ptr; lengths[0] = ccb->ataio.dxfer_len; dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; numbufs = 1; break; case XPT_SMP_IO: data_ptrs[0] = &ccb->smpio.smp_request; lengths[0] = ccb->smpio.smp_request_len; dirs[0] = CAM_DIR_OUT; data_ptrs[1] = &ccb->smpio.smp_response; lengths[1] = ccb->smpio.smp_response_len; dirs[1] = CAM_DIR_IN; numbufs = 2; break; case XPT_DEV_ADVINFO: if (ccb->cdai.bufsiz == 0) return (0); data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; lengths[0] = ccb->cdai.bufsiz; dirs[0] = CAM_DIR_IN; numbufs = 1; /* * This request will not go to the hardware, no reason * to be so strict. vmapbuf() is able to map up to MAXPHYS. */ maxmap = MAXPHYS; break; default: return(EINVAL); break; /* NOTREACHED */ } /* * Check the transfer length and permissions first, so we don't * have to unmap any previously mapped buffers. */ for (i = 0; i < numbufs; i++) { flags[i] = 0; /* * The userland data pointer passed in may not be page * aligned. vmapbuf() truncates the address to a page * boundary, so if the address isn't page aligned, we'll * need enough space for the given transfer length, plus * whatever extra space is necessary to make it to the page * boundary. */ if ((lengths[i] + (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > maxmap){ printf("cam_periph_mapmem: attempt to map %lu bytes, " "which is greater than %lu\n", (long)(lengths[i] + (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), (u_long)maxmap); return(E2BIG); } if (dirs[i] & CAM_DIR_OUT) { flags[i] = BIO_WRITE; } if (dirs[i] & CAM_DIR_IN) { flags[i] = BIO_READ; } } /* * This keeps the the kernel stack of current thread from getting * swapped. In low-memory situations where the kernel stack might * otherwise get swapped out, this holds it and allows the thread * to make progress and release the kernel mapped pages sooner. * * XXX KDM should I use P_NOSWAP instead? */ PHOLD(curproc); for (i = 0; i < numbufs; i++) { /* * Get the buffer. */ mapinfo->bp[i] = getpbuf(NULL); /* save the buffer's data address */ mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; /* put our pointer in the data slot */ mapinfo->bp[i]->b_data = *data_ptrs[i]; /* set the transfer length, we know it's < MAXPHYS */ mapinfo->bp[i]->b_bufsize = lengths[i]; /* set the direction */ mapinfo->bp[i]->b_iocmd = flags[i]; /* * Map the buffer into kernel memory. * * Note that useracc() alone is not a sufficient test. * vmapbuf() can still fail due to a smaller file mapped * into a larger area of VM, or if userland races against * vmapbuf() after the useracc() check. */ if (vmapbuf(mapinfo->bp[i], 1) < 0) { for (j = 0; j < i; ++j) { *data_ptrs[j] = mapinfo->bp[j]->b_saveaddr; vunmapbuf(mapinfo->bp[j]); relpbuf(mapinfo->bp[j], NULL); } relpbuf(mapinfo->bp[i], NULL); PRELE(curproc); return(EACCES); } /* set our pointer to the new mapped area */ *data_ptrs[i] = mapinfo->bp[i]->b_data; mapinfo->num_bufs_used++; } /* * Now that we've gotten this far, change ownership to the kernel * of the buffers so that we don't run afoul of returning to user * space with locks (on the buffer) held. */ for (i = 0; i < numbufs; i++) { BUF_KERNPROC(mapinfo->bp[i]); } return(0); } /* * Unmap memory segments mapped into kernel virtual address space by * cam_periph_mapmem(). */ void cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) { int numbufs, i; u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; if (mapinfo->num_bufs_used <= 0) { /* nothing to free and the process wasn't held. */ return; } switch (ccb->ccb_h.func_code) { case XPT_DEV_MATCH: numbufs = min(mapinfo->num_bufs_used, 2); if (numbufs == 1) { data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; } else { data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; } break; case XPT_SCSI_IO: case XPT_CONT_TARGET_IO: data_ptrs[0] = &ccb->csio.data_ptr; numbufs = min(mapinfo->num_bufs_used, 1); break; case XPT_ATA_IO: data_ptrs[0] = &ccb->ataio.data_ptr; numbufs = min(mapinfo->num_bufs_used, 1); break; case XPT_SMP_IO: numbufs = min(mapinfo->num_bufs_used, 2); data_ptrs[0] = &ccb->smpio.smp_request; data_ptrs[1] = &ccb->smpio.smp_response; break; case XPT_DEV_ADVINFO: numbufs = min(mapinfo->num_bufs_used, 1); data_ptrs[0] = (uint8_t **)&ccb->cdai.buf; break; default: /* allow ourselves to be swapped once again */ PRELE(curproc); return; break; /* NOTREACHED */ } for (i = 0; i < numbufs; i++) { /* Set the user's pointer back to the original value */ *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; /* unmap the buffer */ vunmapbuf(mapinfo->bp[i]); /* release the buffer */ relpbuf(mapinfo->bp[i], NULL); } /* allow ourselves to be swapped once again */ PRELE(curproc); } void cam_periph_ccbwait(union ccb *ccb) { if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) xpt_path_sleep(ccb->ccb_h.path, &ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); } int cam_periph_ioctl(struct cam_periph *periph, u_long cmd, caddr_t addr, int (*error_routine)(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags)) { union ccb *ccb; int error; int found; error = found = 0; switch(cmd){ case CAMGETPASSTHRU: ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); ccb->ccb_h.func_code = XPT_GDEVLIST; /* * Basically, the point of this is that we go through * getting the list of devices, until we find a passthrough * device. In the current version of the CAM code, the * only way to determine what type of device we're dealing * with is by its name. */ while (found == 0) { ccb->cgdl.index = 0; ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { /* we want the next device in the list */ xpt_action(ccb); if (strncmp(ccb->cgdl.periph_name, "pass", 4) == 0){ found = 1; break; } } if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && (found == 0)) { ccb->cgdl.periph_name[0] = '\0'; ccb->cgdl.unit_number = 0; break; } } /* copy the result back out */ bcopy(ccb, addr, sizeof(union ccb)); /* and release the ccb */ xpt_release_ccb(ccb); break; default: error = ENOTTY; break; } return(error); } static void cam_periph_done(struct cam_periph *periph, union ccb *done_ccb) { /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); } int cam_periph_runccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags), cam_flags camflags, u_int32_t sense_flags, struct devstat *ds) { int error; xpt_path_assert(ccb->ccb_h.path, MA_OWNED); /* * If the user has supplied a stats structure, and if we understand * this particular type of ccb, record the transaction start. */ if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO || ccb->ccb_h.func_code == XPT_ATA_IO)) devstat_start_transaction(ds, NULL); ccb->ccb_h.cbfcnp = cam_periph_done; xpt_action(ccb); do { cam_periph_ccbwait(ccb); if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) error = 0; else if (error_routine != NULL) error = (*error_routine)(ccb, camflags, sense_flags); else error = 0; } while (error == ERESTART); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(ccb->ccb_h.path, /* relsim_flags */0, /* openings */0, /* timeout */0, /* getcount_only */ FALSE); ccb->ccb_h.status &= ~CAM_DEV_QFRZN; } if (ds != NULL) { if (ccb->ccb_h.func_code == XPT_SCSI_IO) { devstat_end_transaction(ds, ccb->csio.dxfer_len, ccb->csio.tag_action & 0x3, ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) ? DEVSTAT_NO_DATA : (ccb->ccb_h.flags & CAM_DIR_OUT) ? DEVSTAT_WRITE : DEVSTAT_READ, NULL, NULL); } else if (ccb->ccb_h.func_code == XPT_ATA_IO) { devstat_end_transaction(ds, ccb->ataio.dxfer_len, ccb->ataio.tag_action & 0x3, ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) ? DEVSTAT_NO_DATA : (ccb->ccb_h.flags & CAM_DIR_OUT) ? DEVSTAT_WRITE : DEVSTAT_READ, NULL, NULL); } } return(error); } void cam_freeze_devq(struct cam_path *path) { struct ccb_hdr ccb_h; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_freeze_devq\n")); xpt_setup_ccb(&ccb_h, path, /*priority*/1); ccb_h.func_code = XPT_NOOP; ccb_h.flags = CAM_DEV_QFREEZE; xpt_action((union ccb *)&ccb_h); } u_int32_t cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, u_int32_t openings, u_int32_t arg, int getcount_only) { struct ccb_relsim crs; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("cam_release_devq(%u, %u, %u, %d)\n", relsim_flags, openings, arg, getcount_only)); xpt_setup_ccb(&crs.ccb_h, path, CAM_PRIORITY_NORMAL); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; crs.release_flags = relsim_flags; crs.openings = openings; crs.release_timeout = arg; xpt_action((union ccb *)&crs); return (crs.qfrozen_cnt); } #define saved_ccb_ptr ppriv_ptr0 static void camperiphdone(struct cam_periph *periph, union ccb *done_ccb) { union ccb *saved_ccb; cam_status status; struct scsi_start_stop_unit *scsi_cmd; int error_code, sense_key, asc, ascq; scsi_cmd = (struct scsi_start_stop_unit *) &done_ccb->csio.cdb_io.cdb_bytes; status = done_ccb->ccb_h.status; if ((status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (scsi_extract_sense_ccb(done_ccb, &error_code, &sense_key, &asc, &ascq)) { /* * If the error is "invalid field in CDB", * and the load/eject flag is set, turn the * flag off and try again. This is just in * case the drive in question barfs on the * load eject flag. The CAM code should set * the load/eject flag by default for * removable media. */ if ((scsi_cmd->opcode == START_STOP_UNIT) && ((scsi_cmd->how & SSS_LOEJ) != 0) && (asc == 0x24) && (ascq == 0x00)) { scsi_cmd->how &= ~SSS_LOEJ; if (status & CAM_DEV_QFRZN) { cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; } xpt_action(done_ccb); goto out; } } if (cam_periph_error(done_ccb, 0, SF_RETRY_UA | SF_NO_PRINT, NULL) == ERESTART) goto out; if (done_ccb->ccb_h.status & CAM_DEV_QFRZN) { cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; } } else { /* * If we have successfully taken a device from the not * ready to ready state, re-scan the device and re-get * the inquiry information. Many devices (mostly disks) * don't properly report their inquiry information unless * they are spun up. */ if (scsi_cmd->opcode == START_STOP_UNIT) xpt_async(AC_INQ_CHANGED, done_ccb->ccb_h.path, NULL); } /* * Perform the final retry with the original CCB so that final * error processing is performed by the owner of the CCB. */ saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; bcopy(saved_ccb, done_ccb, sizeof(*done_ccb)); xpt_free_ccb(saved_ccb); if (done_ccb->ccb_h.cbfcnp != camperiphdone) periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; xpt_action(done_ccb); out: /* Drop freeze taken due to CAM_DEV_QFREEZE flag set. */ cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0); } /* * Generic Async Event handler. Peripheral drivers usually * filter out the events that require personal attention, * and leave the rest to this function. */ void cam_periph_async(struct cam_periph *periph, u_int32_t code, struct cam_path *path, void *arg) { switch (code) { case AC_LOST_DEVICE: cam_periph_invalidate(periph); break; default: break; } } void cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) { struct ccb_getdevstats cgds; xpt_setup_ccb(&cgds.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cgds.ccb_h.func_code = XPT_GDEV_STATS; xpt_action((union ccb *)&cgds); cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); } void cam_periph_freeze_after_event(struct cam_periph *periph, struct timeval* event_time, u_int duration_ms) { struct timeval delta; struct timeval duration_tv; if (!timevalisset(event_time)) return; microtime(&delta); timevalsub(&delta, event_time); duration_tv.tv_sec = duration_ms / 1000; duration_tv.tv_usec = (duration_ms % 1000) * 1000; if (timevalcmp(&delta, &duration_tv, <)) { timevalsub(&duration_tv, &delta); duration_ms = duration_tv.tv_sec * 1000; duration_ms += duration_tv.tv_usec / 1000; cam_freeze_devq(periph->path); cam_release_devq(periph->path, RELSIM_RELEASE_AFTER_TIMEOUT, /*reduction*/0, /*timeout*/duration_ms, /*getcount_only*/0); } } static int camperiphscsistatuserror(union ccb *ccb, union ccb **orig_ccb, cam_flags camflags, u_int32_t sense_flags, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout, u_int32_t *action, const char **action_string) { int 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: error = 0; break; case SCSI_STATUS_CMD_TERMINATED: case SCSI_STATUS_CHECK_COND: error = camperiphscsisenseerror(ccb, orig_ccb, camflags, sense_flags, openings, relsim_flags, timeout, action, action_string); break; case SCSI_STATUS_QUEUE_FULL: { /* no decrement */ struct ccb_getdevstats cgds; /* * First off, find out what the current * transaction counts are. */ xpt_setup_ccb(&cgds.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); cgds.ccb_h.func_code = XPT_GDEV_STATS; xpt_action((union ccb *)&cgds); /* * If we were the only transaction active, treat * the QUEUE FULL as if it were a BUSY condition. */ if (cgds.dev_active != 0) { int total_openings; /* * Reduce the number of openings to * be 1 less than the amount it took * to get a queue full bounded by the * minimum allowed tag count for this * device. */ total_openings = cgds.dev_active + cgds.dev_openings; *openings = cgds.dev_active; if (*openings < cgds.mintags) *openings = cgds.mintags; if (*openings < total_openings) *relsim_flags = RELSIM_ADJUST_OPENINGS; else { /* * Some devices report queue full for * temporary resource shortages. For * this reason, we allow a minimum * tag count to be entered via a * quirk entry to prevent the queue * count on these devices from falling * to a pessimisticly low value. We * still wait for the next successful * completion, however, before queueing * more transactions to the device. */ *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; } *timeout = 0; error = ERESTART; *action &= ~SSQ_PRINT_SENSE; break; } /* FALLTHROUGH */ } case SCSI_STATUS_BUSY: /* * Restart the queue after either another * command completes or a 1 second timeout. */ - if (ccb->ccb_h.retry_count > 0) { - ccb->ccb_h.retry_count--; + if ((sense_flags & SF_RETRY_BUSY) != 0 || + (ccb->ccb_h.retry_count--) > 0) { error = ERESTART; *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT | RELSIM_RELEASE_AFTER_CMDCMPLT; *timeout = 1000; } else { error = EIO; } break; case SCSI_STATUS_RESERV_CONFLICT: default: error = EIO; break; } return (error); } static int camperiphscsisenseerror(union ccb *ccb, union ccb **orig, cam_flags camflags, u_int32_t sense_flags, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout, u_int32_t *action, const char **action_string) { struct cam_periph *periph; union ccb *orig_ccb = ccb; int error, recoveryccb; periph = xpt_path_periph(ccb->ccb_h.path); recoveryccb = (ccb->ccb_h.cbfcnp == camperiphdone); if ((periph->flags & CAM_PERIPH_RECOVERY_INPROG) && !recoveryccb) { /* * If error recovery is already in progress, don't attempt * to process this error, but requeue it unconditionally * and attempt to process it once error recovery has * completed. This failed command is probably related to * the error that caused the currently active error recovery * action so our current recovery efforts should also * address this command. Be aware that the error recovery * code assumes that only one recovery action is in progress * on a particular peripheral instance at any given time * (e.g. only one saved CCB for error recovery) so it is * imperitive that we don't violate this assumption. */ error = ERESTART; *action &= ~SSQ_PRINT_SENSE; } else { scsi_sense_action err_action; struct ccb_getdev cgd; /* * Grab the inquiry data for this device. */ xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); err_action = scsi_error_action(&ccb->csio, &cgd.inq_data, sense_flags); error = err_action & SS_ERRMASK; /* * Do not autostart sequential access devices * to avoid unexpected tape loading. */ if ((err_action & SS_MASK) == SS_START && SID_TYPE(&cgd.inq_data) == T_SEQUENTIAL) { *action_string = "Will not autostart a " "sequential access device"; goto sense_error_done; } /* * Avoid recovery recursion if recovery action is the same. */ if ((err_action & SS_MASK) >= SS_START && recoveryccb) { if (((err_action & SS_MASK) == SS_START && ccb->csio.cdb_io.cdb_bytes[0] == START_STOP_UNIT) || ((err_action & SS_MASK) == SS_TUR && (ccb->csio.cdb_io.cdb_bytes[0] == TEST_UNIT_READY))) { err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; *timeout = 500; } } /* * If the recovery action will consume a retry, * make sure we actually have retries available. */ if ((err_action & SSQ_DECREMENT_COUNT) != 0) { if (ccb->ccb_h.retry_count > 0 && (periph->flags & CAM_PERIPH_INVALID) == 0) ccb->ccb_h.retry_count--; else { *action_string = "Retries exhausted"; goto sense_error_done; } } if ((err_action & SS_MASK) >= SS_START) { /* * Do common portions of commands that * use recovery CCBs. */ orig_ccb = xpt_alloc_ccb_nowait(); if (orig_ccb == NULL) { *action_string = "Can't allocate recovery CCB"; goto sense_error_done; } /* * Clear freeze flag for original request here, as * this freeze will be dropped as part of ERESTART. */ ccb->ccb_h.status &= ~CAM_DEV_QFRZN; bcopy(ccb, orig_ccb, sizeof(*orig_ccb)); } switch (err_action & SS_MASK) { case SS_NOP: *action_string = "No recovery action needed"; error = 0; break; case SS_RETRY: *action_string = "Retrying command (per sense data)"; error = ERESTART; break; case SS_FAIL: *action_string = "Unretryable error"; break; case SS_START: { int le; /* * Send a start unit command to the device, and * then retry the command. */ *action_string = "Attempting to start unit"; periph->flags |= CAM_PERIPH_RECOVERY_INPROG; /* * Check for removable media and set * load/eject flag appropriately. */ if (SID_IS_REMOVABLE(&cgd.inq_data)) le = TRUE; else le = FALSE; scsi_start_stop(&ccb->csio, /*retries*/1, camperiphdone, MSG_SIMPLE_Q_TAG, /*start*/TRUE, /*load/eject*/le, /*immediate*/FALSE, SSD_FULL_SIZE, /*timeout*/50000); break; } case SS_TUR: { /* * Send a Test Unit Ready to the device. * If the 'many' flag is set, we send 120 * test unit ready commands, one every half * second. Otherwise, we just send one TUR. * We only want to do this if the retry * count has not been exhausted. */ int retries; if ((err_action & SSQ_MANY) != 0) { *action_string = "Polling device for readiness"; retries = 120; } else { *action_string = "Testing device for readiness"; retries = 1; } periph->flags |= CAM_PERIPH_RECOVERY_INPROG; scsi_test_unit_ready(&ccb->csio, retries, camperiphdone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/5000); /* * Accomplish our 500ms delay by deferring * the release of our device queue appropriately. */ *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; *timeout = 500; break; } default: panic("Unhandled error action %x", err_action); } if ((err_action & SS_MASK) >= SS_START) { /* * Drop the priority, so that the recovery * CCB is the first to execute. Freeze the queue * after this command is sent so that we can * restore the old csio and have it queued in * the proper order before we release normal * transactions to the device. */ ccb->ccb_h.pinfo.priority--; ccb->ccb_h.flags |= CAM_DEV_QFREEZE; ccb->ccb_h.saved_ccb_ptr = orig_ccb; error = ERESTART; *orig = orig_ccb; } sense_error_done: *action = err_action; } return (error); } /* * Generic error handler. Peripheral drivers usually filter * out the errors that they handle in a unique mannor, then * call this function. */ int cam_periph_error(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb) { struct cam_path *newpath; union ccb *orig_ccb, *scan_ccb; struct cam_periph *periph; const char *action_string; cam_status status; int frozen, error, openings; u_int32_t action, relsim_flags, timeout; action = SSQ_PRINT_SENSE; periph = xpt_path_periph(ccb->ccb_h.path); action_string = NULL; status = ccb->ccb_h.status; frozen = (status & CAM_DEV_QFRZN) != 0; status &= CAM_STATUS_MASK; openings = relsim_flags = timeout = 0; orig_ccb = ccb; switch (status) { case CAM_REQ_CMP: error = 0; action &= ~SSQ_PRINT_SENSE; break; case CAM_SCSI_STATUS_ERROR: error = camperiphscsistatuserror(ccb, &orig_ccb, camflags, sense_flags, &openings, &relsim_flags, &timeout, &action, &action_string); break; case CAM_AUTOSENSE_FAIL: error = EIO; /* we have to kill the command */ break; case CAM_UA_ABORT: case CAM_UA_TERMIO: case CAM_MSG_REJECT_REC: /* XXX Don't know that these are correct */ error = EIO; break; case CAM_SEL_TIMEOUT: if ((camflags & CAM_RETRY_SELTO) != 0) { if (ccb->ccb_h.retry_count > 0 && (periph->flags & CAM_PERIPH_INVALID) == 0) { ccb->ccb_h.retry_count--; error = ERESTART; /* * Wait a bit to give the device * time to recover before we try again. */ relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; timeout = periph_selto_delay; break; } action_string = "Retries exhausted"; } /* FALLTHROUGH */ case CAM_DEV_NOT_THERE: error = ENXIO; action = SSQ_LOST; break; case CAM_REQ_INVALID: case CAM_PATH_INVALID: case CAM_NO_HBA: case CAM_PROVIDE_FAIL: case CAM_REQ_TOO_BIG: case CAM_LUN_INVALID: case CAM_TID_INVALID: case CAM_FUNC_NOTAVAIL: error = EINVAL; break; case CAM_SCSI_BUS_RESET: case CAM_BDR_SENT: /* * Commands that repeatedly timeout and cause these * kinds of error recovery actions, should return * CAM_CMD_TIMEOUT, which allows us to safely assume * that this command was an innocent bystander to * these events and should be unconditionally * retried. */ case CAM_REQUEUE_REQ: /* Unconditional requeue if device is still there */ if (periph->flags & CAM_PERIPH_INVALID) { action_string = "Periph was invalidated"; error = EIO; } else if (sense_flags & SF_NO_RETRY) { error = EIO; action_string = "Retry was blocked"; } else { error = ERESTART; action &= ~SSQ_PRINT_SENSE; } break; case CAM_RESRC_UNAVAIL: /* Wait a bit for the resource shortage to abate. */ timeout = periph_noresrc_delay; /* FALLTHROUGH */ case CAM_BUSY: if (timeout == 0) { /* Wait a bit for the busy condition to abate. */ timeout = periph_busy_delay; } relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; /* FALLTHROUGH */ case CAM_ATA_STATUS_ERROR: case CAM_REQ_CMP_ERR: case CAM_CMD_TIMEOUT: case CAM_UNEXP_BUSFREE: case CAM_UNCOR_PARITY: case CAM_DATA_RUN_ERR: default: if (periph->flags & CAM_PERIPH_INVALID) { error = EIO; action_string = "Periph was invalidated"; } else if (ccb->ccb_h.retry_count == 0) { error = EIO; action_string = "Retries exhausted"; } else if (sense_flags & SF_NO_RETRY) { error = EIO; action_string = "Retry was blocked"; } else { ccb->ccb_h.retry_count--; error = ERESTART; } break; } if ((sense_flags & SF_PRINT_ALWAYS) || CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) action |= SSQ_PRINT_SENSE; else if (sense_flags & SF_NO_PRINT) action &= ~SSQ_PRINT_SENSE; if ((action & SSQ_PRINT_SENSE) != 0) cam_error_print(orig_ccb, CAM_ESF_ALL, CAM_EPF_ALL); if (error != 0 && (action & SSQ_PRINT_SENSE) != 0) { if (error != ERESTART) { if (action_string == NULL) action_string = "Unretryable error"; xpt_print(ccb->ccb_h.path, "Error %d, %s\n", error, action_string); } else if (action_string != NULL) xpt_print(ccb->ccb_h.path, "%s\n", action_string); else xpt_print(ccb->ccb_h.path, "Retrying command\n"); } if ((action & SSQ_LOST) != 0) { lun_id_t lun_id; /* * For a selection timeout, we consider all of the LUNs on * the target to be gone. If the status is CAM_DEV_NOT_THERE, * then we only get rid of the device(s) specified by the * path in the original CCB. */ if (status == CAM_SEL_TIMEOUT) lun_id = CAM_LUN_WILDCARD; else lun_id = xpt_path_lun_id(ccb->ccb_h.path); /* Should we do more if we can't create the path?? */ if (xpt_create_path(&newpath, periph, xpt_path_path_id(ccb->ccb_h.path), xpt_path_target_id(ccb->ccb_h.path), lun_id) == CAM_REQ_CMP) { /* * Let peripheral drivers know that this * device has gone away. */ xpt_async(AC_LOST_DEVICE, newpath, NULL); xpt_free_path(newpath); } } /* Broadcast UNIT ATTENTIONs to all periphs. */ if ((action & SSQ_UA) != 0) xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb); /* Rescan target on "Reported LUNs data has changed" */ if ((action & SSQ_RESCAN) != 0) { if (xpt_create_path(&newpath, NULL, xpt_path_path_id(ccb->ccb_h.path), xpt_path_target_id(ccb->ccb_h.path), CAM_LUN_WILDCARD) == CAM_REQ_CMP) { scan_ccb = xpt_alloc_ccb_nowait(); if (scan_ccb != NULL) { scan_ccb->ccb_h.path = newpath; scan_ccb->ccb_h.func_code = XPT_SCAN_TGT; scan_ccb->crcn.flags = 0; xpt_rescan(scan_ccb); } else { xpt_print(newpath, "Can't allocate CCB to rescan target\n"); xpt_free_path(newpath); } } } /* Attempt a retry */ if (error == ERESTART || error == 0) { if (frozen != 0) ccb->ccb_h.status &= ~CAM_DEV_QFRZN; if (error == ERESTART) xpt_action(ccb); if (frozen != 0) cam_release_devq(ccb->ccb_h.path, relsim_flags, openings, timeout, /*getcount_only*/0); } return (error); } Index: projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.c =================================================================== --- projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.c (revision 278195) +++ projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.c (revision 278196) @@ -1,2884 +1,2894 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala 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$ */ /* * CTL frontend for the iSCSI protocol. */ #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 #ifdef ICL_KERNEL_PROXY #include #endif #ifdef ICL_KERNEL_PROXY FEATURE(cfiscsi_kernel_proxy, "iSCSI target built with ICL_KERNEL_PROXY"); #endif static MALLOC_DEFINE(M_CFISCSI, "cfiscsi", "Memory used for CTL iSCSI frontend"); static uma_zone_t cfiscsi_data_wait_zone; SYSCTL_NODE(_kern_cam_ctl, OID_AUTO, iscsi, CTLFLAG_RD, 0, "CAM Target Layer iSCSI Frontend"); static int debug = 1; SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 1, "Enable debug messages"); static int ping_timeout = 5; SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN, &ping_timeout, 5, "Interval between ping (NOP-Out) requests, in seconds"); static int login_timeout = 60; SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, login_timeout, CTLFLAG_RWTUN, &login_timeout, 60, "Time to wait for ctld(8) to finish Login Phase, in seconds"); static int maxcmdsn_delta = 256; SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxcmdsn_delta, CTLFLAG_RWTUN, &maxcmdsn_delta, 256, "Number of commands the initiator can send " "without confirmation"); #define CFISCSI_DEBUG(X, ...) \ do { \ if (debug > 1) { \ printf("%s: " X "\n", \ __func__, ## __VA_ARGS__); \ } \ } while (0) #define CFISCSI_WARN(X, ...) \ do { \ if (debug > 0) { \ printf("WARNING: %s: " X "\n", \ __func__, ## __VA_ARGS__); \ } \ } while (0) #define CFISCSI_SESSION_DEBUG(S, X, ...) \ do { \ if (debug > 1) { \ printf("%s: %s (%s): " X "\n", \ __func__, S->cs_initiator_addr, \ S->cs_initiator_name, ## __VA_ARGS__); \ } \ } while (0) #define CFISCSI_SESSION_WARN(S, X, ...) \ do { \ if (debug > 0) { \ printf("WARNING: %s (%s): " X "\n", \ S->cs_initiator_addr, \ S->cs_initiator_name, ## __VA_ARGS__); \ } \ } while (0) #define CFISCSI_SESSION_LOCK(X) mtx_lock(&X->cs_lock) #define CFISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->cs_lock) #define CFISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->cs_lock, MA_OWNED) #define CONN_SESSION(X) ((struct cfiscsi_session *)(X)->ic_prv0) #define PDU_SESSION(X) CONN_SESSION((X)->ip_conn) #define PDU_EXPDATASN(X) (X)->ip_prv0 #define PDU_TOTAL_TRANSFER_LEN(X) (X)->ip_prv1 #define PDU_R2TSN(X) (X)->ip_prv2 int cfiscsi_init(void); static void cfiscsi_online(void *arg); static void cfiscsi_offline(void *arg); static int cfiscsi_info(void *arg, struct sbuf *sb); static int cfiscsi_lun_enable(void *arg, struct ctl_id target_id, int lun_id); static int cfiscsi_lun_disable(void *arg, struct ctl_id target_id, int lun_id); static int cfiscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td); static void cfiscsi_datamove(union ctl_io *io); static void cfiscsi_datamove_in(union ctl_io *io); static void cfiscsi_datamove_out(union ctl_io *io); static void cfiscsi_done(union ctl_io *io); static bool cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request); static void cfiscsi_pdu_handle_nop_out(struct icl_pdu *request); static void cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request); static void cfiscsi_pdu_handle_task_request(struct icl_pdu *request); static void cfiscsi_pdu_handle_data_out(struct icl_pdu *request); static void cfiscsi_pdu_handle_logout_request(struct icl_pdu *request); static void cfiscsi_session_terminate(struct cfiscsi_session *cs); static struct cfiscsi_target *cfiscsi_target_find(struct cfiscsi_softc - *softc, const char *name); + *softc, const char *name, uint16_t tag); static struct cfiscsi_target *cfiscsi_target_find_or_create( - struct cfiscsi_softc *softc, const char *name, const char *alias); + struct cfiscsi_softc *softc, const char *name, const char *alias, + uint16_t tag); static void cfiscsi_target_release(struct cfiscsi_target *ct); static void cfiscsi_session_delete(struct cfiscsi_session *cs); static struct cfiscsi_softc cfiscsi_softc; extern struct ctl_softc *control_softc; static struct ctl_frontend cfiscsi_frontend = { .name = "iscsi", .init = cfiscsi_init, .ioctl = cfiscsi_ioctl, }; CTL_FRONTEND_DECLARE(ctlcfiscsi, cfiscsi_frontend); MODULE_DEPEND(ctlcfiscsi, icl, 1, 1, 1); static struct icl_pdu * cfiscsi_pdu_new_response(struct icl_pdu *request, int flags) { return (icl_pdu_new(request->ip_conn, flags)); } static bool cfiscsi_pdu_update_cmdsn(const struct icl_pdu *request) { const struct iscsi_bhs_scsi_command *bhssc; struct cfiscsi_session *cs; uint32_t cmdsn, expstatsn; cs = PDU_SESSION(request); /* * Every incoming PDU - not just NOP-Out - resets the ping timer. * The purpose of the timeout is to reset the connection when it stalls; * we don't want this to happen when NOP-In or NOP-Out ends up delayed * in some queue. * * XXX: Locking? */ cs->cs_timeout = 0; /* * Data-Out PDUs don't contain CmdSN. */ if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_DATA_OUT) return (false); /* * We're only using fields common for all the request * (initiator -> target) PDUs. */ bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs; cmdsn = ntohl(bhssc->bhssc_cmdsn); expstatsn = ntohl(bhssc->bhssc_expstatsn); CFISCSI_SESSION_LOCK(cs); #if 0 if (expstatsn != cs->cs_statsn) { CFISCSI_SESSION_DEBUG(cs, "received PDU with ExpStatSN %d, " "while current StatSN is %d", expstatsn, cs->cs_statsn); } #endif if ((request->ip_bhs->bhs_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0) { /* * The target MUST silently ignore any non-immediate command * outside of this range. */ if (ISCSI_SNLT(cmdsn, cs->cs_cmdsn) || ISCSI_SNGT(cmdsn, cs->cs_cmdsn + maxcmdsn_delta)) { CFISCSI_SESSION_UNLOCK(cs); CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %u, " "while expected %u", cmdsn, cs->cs_cmdsn); return (true); } /* * We don't support multiple connections now, so any * discontinuity in CmdSN means lost PDUs. Since we don't * support PDU retransmission -- terminate the connection. */ if (cmdsn != cs->cs_cmdsn) { CFISCSI_SESSION_UNLOCK(cs); CFISCSI_SESSION_WARN(cs, "received PDU with CmdSN %u, " "while expected %u; dropping connection", cmdsn, cs->cs_cmdsn); cfiscsi_session_terminate(cs); return (true); } cs->cs_cmdsn++; } CFISCSI_SESSION_UNLOCK(cs); return (false); } static void cfiscsi_pdu_handle(struct icl_pdu *request) { struct cfiscsi_session *cs; bool ignore; cs = PDU_SESSION(request); ignore = cfiscsi_pdu_update_cmdsn(request); if (ignore) { icl_pdu_free(request); return; } /* * Handle the PDU; this includes e.g. receiving the remaining * part of PDU and submitting the SCSI command to CTL * or queueing a reply. The handling routine is responsible * for freeing the PDU when it's no longer needed. */ switch (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) { case ISCSI_BHS_OPCODE_NOP_OUT: cfiscsi_pdu_handle_nop_out(request); break; case ISCSI_BHS_OPCODE_SCSI_COMMAND: cfiscsi_pdu_handle_scsi_command(request); break; case ISCSI_BHS_OPCODE_TASK_REQUEST: cfiscsi_pdu_handle_task_request(request); break; case ISCSI_BHS_OPCODE_SCSI_DATA_OUT: cfiscsi_pdu_handle_data_out(request); break; case ISCSI_BHS_OPCODE_LOGOUT_REQUEST: cfiscsi_pdu_handle_logout_request(request); break; default: CFISCSI_SESSION_WARN(cs, "received PDU with unsupported " "opcode 0x%x; dropping connection", request->ip_bhs->bhs_opcode); icl_pdu_free(request); cfiscsi_session_terminate(cs); } } static void cfiscsi_receive_callback(struct icl_pdu *request) { struct cfiscsi_session *cs; cs = PDU_SESSION(request); #ifdef ICL_KERNEL_PROXY if (cs->cs_waiting_for_ctld || cs->cs_login_phase) { if (cs->cs_login_pdu == NULL) cs->cs_login_pdu = request; else icl_pdu_free(request); cv_signal(&cs->cs_login_cv); return; } #endif cfiscsi_pdu_handle(request); } static void cfiscsi_error_callback(struct icl_conn *ic) { struct cfiscsi_session *cs; cs = CONN_SESSION(ic); CFISCSI_SESSION_WARN(cs, "connection error; dropping connection"); cfiscsi_session_terminate(cs); } static int cfiscsi_pdu_prepare(struct icl_pdu *response) { struct cfiscsi_session *cs; struct iscsi_bhs_scsi_response *bhssr; bool advance_statsn = true; cs = PDU_SESSION(response); CFISCSI_SESSION_LOCK_ASSERT(cs); /* * We're only using fields common for all the response * (target -> initiator) PDUs. */ bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; /* * 10.8.3: "The StatSN for this connection is not advanced * after this PDU is sent." */ if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_R2T) advance_statsn = false; /* * 10.19.2: "However, when the Initiator Task Tag is set to 0xffffffff, * StatSN for the connection is not advanced after this PDU is sent." */ if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_NOP_IN && bhssr->bhssr_initiator_task_tag == 0xffffffff) advance_statsn = false; /* * See the comment below - StatSN is not meaningful and must * not be advanced. */ if (bhssr->bhssr_opcode == ISCSI_BHS_OPCODE_SCSI_DATA_IN && (bhssr->bhssr_flags & BHSDI_FLAGS_S) == 0) advance_statsn = false; /* * 10.7.3: "The fields StatSN, Status, and Residual Count * only have meaningful content if the S bit is set to 1." */ if (bhssr->bhssr_opcode != ISCSI_BHS_OPCODE_SCSI_DATA_IN || (bhssr->bhssr_flags & BHSDI_FLAGS_S)) bhssr->bhssr_statsn = htonl(cs->cs_statsn); bhssr->bhssr_expcmdsn = htonl(cs->cs_cmdsn); bhssr->bhssr_maxcmdsn = htonl(cs->cs_cmdsn + maxcmdsn_delta); if (advance_statsn) cs->cs_statsn++; return (0); } static void cfiscsi_pdu_queue(struct icl_pdu *response) { struct cfiscsi_session *cs; cs = PDU_SESSION(response); CFISCSI_SESSION_LOCK(cs); cfiscsi_pdu_prepare(response); icl_pdu_queue(response); CFISCSI_SESSION_UNLOCK(cs); } static uint32_t cfiscsi_decode_lun(uint64_t encoded) { uint8_t lun[8]; uint32_t result; /* * The LUN field in iSCSI PDUs may look like an ordinary 64 bit number, * but is in fact an evil, multidimensional structure defined * in SCSI Architecture Model 5 (SAM-5), section 4.6. */ memcpy(lun, &encoded, sizeof(lun)); switch (lun[0] & 0xC0) { case 0x00: if ((lun[0] & 0x3f) != 0 || lun[2] != 0 || lun[3] != 0 || lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) { CFISCSI_WARN("malformed LUN " "(peripheral device addressing method): 0x%jx", (uintmax_t)encoded); result = 0xffffffff; break; } result = lun[1]; break; case 0x40: if (lun[2] != 0 || lun[3] != 0 || lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) { CFISCSI_WARN("malformed LUN " "(flat address space addressing method): 0x%jx", (uintmax_t)encoded); result = 0xffffffff; break; } result = ((lun[0] & 0x3f) << 8) + lun[1]; break; case 0xC0: if (lun[0] != 0xD2 || lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0) { CFISCSI_WARN("malformed LUN (extended flat " "address space addressing method): 0x%jx", (uintmax_t)encoded); result = 0xffffffff; break; } result = (lun[1] << 16) + (lun[2] << 8) + lun[3]; default: CFISCSI_WARN("unsupported LUN format 0x%jx", (uintmax_t)encoded); result = 0xffffffff; break; } return (result); } static void cfiscsi_pdu_handle_nop_out(struct icl_pdu *request) { struct cfiscsi_session *cs; struct iscsi_bhs_nop_out *bhsno; struct iscsi_bhs_nop_in *bhsni; struct icl_pdu *response; void *data = NULL; size_t datasize; int error; cs = PDU_SESSION(request); bhsno = (struct iscsi_bhs_nop_out *)request->ip_bhs; if (bhsno->bhsno_initiator_task_tag == 0xffffffff) { /* * Nothing to do, iscsi_pdu_update_statsn() already * zeroed the timeout. */ icl_pdu_free(request); return; } datasize = icl_pdu_data_segment_length(request); if (datasize > 0) { data = malloc(datasize, M_CFISCSI, M_NOWAIT | M_ZERO); if (data == NULL) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " "dropping connection"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } icl_pdu_get_data(request, 0, data, datasize); } response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " "droppping connection"); free(data, M_CFISCSI); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } bhsni = (struct iscsi_bhs_nop_in *)response->ip_bhs; bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN; bhsni->bhsni_flags = 0x80; bhsni->bhsni_initiator_task_tag = bhsno->bhsno_initiator_task_tag; bhsni->bhsni_target_transfer_tag = 0xffffffff; if (datasize > 0) { error = icl_pdu_append_data(response, data, datasize, M_NOWAIT); if (error != 0) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " "dropping connection"); free(data, M_CFISCSI); icl_pdu_free(request); icl_pdu_free(response); cfiscsi_session_terminate(cs); return; } free(data, M_CFISCSI); } icl_pdu_free(request); cfiscsi_pdu_queue(response); } static void cfiscsi_pdu_handle_scsi_command(struct icl_pdu *request) { struct iscsi_bhs_scsi_command *bhssc; struct cfiscsi_session *cs; union ctl_io *io; int error; cs = PDU_SESSION(request); bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; //CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x", // bhssc->bhssc_initiator_task_tag); if (request->ip_data_len > 0 && cs->cs_immediate_data == false) { CFISCSI_SESSION_WARN(cs, "unsolicited data with " "ImmediateData=No; dropping connection"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref); ctl_zero_io(io); io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request; io->io_hdr.io_type = CTL_IO_SCSI; io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port; io->io_hdr.nexus.targ_target.id = 0; io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhssc->bhssc_lun); io->scsiio.tag_num = bhssc->bhssc_initiator_task_tag; switch ((bhssc->bhssc_flags & BHSSC_FLAGS_ATTR)) { case BHSSC_FLAGS_ATTR_UNTAGGED: io->scsiio.tag_type = CTL_TAG_UNTAGGED; break; case BHSSC_FLAGS_ATTR_SIMPLE: io->scsiio.tag_type = CTL_TAG_SIMPLE; break; case BHSSC_FLAGS_ATTR_ORDERED: io->scsiio.tag_type = CTL_TAG_ORDERED; break; case BHSSC_FLAGS_ATTR_HOQ: io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE; break; case BHSSC_FLAGS_ATTR_ACA: io->scsiio.tag_type = CTL_TAG_ACA; break; default: io->scsiio.tag_type = CTL_TAG_UNTAGGED; CFISCSI_SESSION_WARN(cs, "unhandled tag type %d", bhssc->bhssc_flags & BHSSC_FLAGS_ATTR); break; } io->scsiio.cdb_len = sizeof(bhssc->bhssc_cdb); /* Which is 16. */ memcpy(io->scsiio.cdb, bhssc->bhssc_cdb, sizeof(bhssc->bhssc_cdb)); refcount_acquire(&cs->cs_outstanding_ctl_pdus); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; " "dropping connection", error); ctl_free_io(io); refcount_release(&cs->cs_outstanding_ctl_pdus); icl_pdu_free(request); cfiscsi_session_terminate(cs); } } static void cfiscsi_pdu_handle_task_request(struct icl_pdu *request) { struct iscsi_bhs_task_management_request *bhstmr; struct iscsi_bhs_task_management_response *bhstmr2; struct icl_pdu *response; struct cfiscsi_session *cs; union ctl_io *io; int error; cs = PDU_SESSION(request); bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref); ctl_zero_io(io); io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = request; io->io_hdr.io_type = CTL_IO_TASK; io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port; io->io_hdr.nexus.targ_target.id = 0; io->io_hdr.nexus.targ_lun = cfiscsi_decode_lun(bhstmr->bhstmr_lun); io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */ switch (bhstmr->bhstmr_function & ~0x80) { case BHSTMR_FUNCTION_ABORT_TASK: #if 0 CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK"); #endif io->taskio.task_action = CTL_TASK_ABORT_TASK; io->taskio.tag_num = bhstmr->bhstmr_referenced_task_tag; break; case BHSTMR_FUNCTION_ABORT_TASK_SET: #if 0 CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_ABORT_TASK_SET"); #endif io->taskio.task_action = CTL_TASK_ABORT_TASK_SET; break; case BHSTMR_FUNCTION_LOGICAL_UNIT_RESET: #if 0 CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_LOGICAL_UNIT_RESET"); #endif io->taskio.task_action = CTL_TASK_LUN_RESET; break; case BHSTMR_FUNCTION_TARGET_WARM_RESET: #if 0 CFISCSI_SESSION_DEBUG(cs, "BHSTMR_FUNCTION_TARGET_WARM_RESET"); #endif io->taskio.task_action = CTL_TASK_TARGET_RESET; break; default: CFISCSI_SESSION_DEBUG(cs, "unsupported function 0x%x", bhstmr->bhstmr_function & ~0x80); ctl_free_io(io); response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory; " "dropping connection"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } bhstmr2 = (struct iscsi_bhs_task_management_response *) response->ip_bhs; bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE; bhstmr2->bhstmr_flags = 0x80; bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED; bhstmr2->bhstmr_initiator_task_tag = bhstmr->bhstmr_initiator_task_tag; icl_pdu_free(request); cfiscsi_pdu_queue(response); return; } refcount_acquire(&cs->cs_outstanding_ctl_pdus); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d; " "dropping connection", error); ctl_free_io(io); refcount_release(&cs->cs_outstanding_ctl_pdus); icl_pdu_free(request); cfiscsi_session_terminate(cs); } } static bool cfiscsi_handle_data_segment(struct icl_pdu *request, struct cfiscsi_data_wait *cdw) { struct iscsi_bhs_data_out *bhsdo; struct cfiscsi_session *cs; struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; size_t copy_len, len, off, buffer_offset; int ctl_sg_count; union ctl_io *io; cs = PDU_SESSION(request); KASSERT((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_DATA_OUT || (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_COMMAND, ("bad opcode 0x%x", request->ip_bhs->bhs_opcode)); /* * We're only using fields common for Data-Out and SCSI Command PDUs. */ bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; io = cdw->cdw_ctl_io; KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN, ("CTL_FLAG_DATA_IN")); #if 0 CFISCSI_SESSION_DEBUG(cs, "received %zd bytes out of %d", request->ip_data_len, io->scsiio.kern_total_len); #endif if (io->scsiio.kern_sg_entries > 0) { ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; ctl_sg_count = io->scsiio.kern_sg_entries; } else { ctl_sglist = &ctl_sg_entry; ctl_sglist->addr = io->scsiio.kern_data_ptr; ctl_sglist->len = io->scsiio.kern_data_len; ctl_sg_count = 1; } if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_DATA_OUT) buffer_offset = ntohl(bhsdo->bhsdo_buffer_offset); else buffer_offset = 0; len = icl_pdu_data_segment_length(request); /* * Make sure the offset, as sent by the initiator, matches the offset * we're supposed to be at in the scatter-gather list. */ if (buffer_offset > io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled || buffer_offset + len <= io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled) { CFISCSI_SESSION_WARN(cs, "received bad buffer offset %zd, " "expected %zd; dropping connection", buffer_offset, (size_t)io->scsiio.kern_rel_offset + (size_t)io->scsiio.ext_data_filled); ctl_set_data_phase_error(&io->scsiio); cfiscsi_session_terminate(cs); return (true); } /* * This is the offset within the PDU data segment, as opposed * to buffer_offset, which is the offset within the task (SCSI * command). */ off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled - buffer_offset; /* * Iterate over the scatter/gather segments, filling them with data * from the PDU data segment. Note that this can get called multiple * times for one SCSI command; the cdw structure holds state for the * scatter/gather list. */ for (;;) { KASSERT(cdw->cdw_sg_index < ctl_sg_count, ("cdw->cdw_sg_index >= ctl_sg_count")); if (cdw->cdw_sg_len == 0) { cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr; cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len; } KASSERT(off <= len, ("len > off")); copy_len = len - off; if (copy_len > cdw->cdw_sg_len) copy_len = cdw->cdw_sg_len; icl_pdu_get_data(request, off, cdw->cdw_sg_addr, copy_len); cdw->cdw_sg_addr += copy_len; cdw->cdw_sg_len -= copy_len; off += copy_len; io->scsiio.ext_data_filled += copy_len; if (cdw->cdw_sg_len == 0) { /* * End of current segment. */ if (cdw->cdw_sg_index == ctl_sg_count - 1) { /* * Last segment in scatter/gather list. */ break; } cdw->cdw_sg_index++; } if (off == len) { /* * End of PDU payload. */ break; } } if (len > off) { /* * In case of unsolicited data, it's possible that the buffer * provided by CTL is smaller than negotiated FirstBurstLength. * Just ignore the superfluous data; will ask for them with R2T * on next call to cfiscsi_datamove(). * * This obviously can only happen with SCSI Command PDU. */ if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_COMMAND) return (true); CFISCSI_SESSION_WARN(cs, "received too much data: got %zd bytes, " "expected %zd; dropping connection", icl_pdu_data_segment_length(request), off); ctl_set_data_phase_error(&io->scsiio); cfiscsi_session_terminate(cs); return (true); } if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end && (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) == 0) { CFISCSI_SESSION_WARN(cs, "got the final packet without " "the F flag; flags = 0x%x; dropping connection", bhsdo->bhsdo_flags); ctl_set_data_phase_error(&io->scsiio); cfiscsi_session_terminate(cs); return (true); } if (io->scsiio.ext_data_filled != cdw->cdw_r2t_end && (bhsdo->bhsdo_flags & BHSDO_FLAGS_F) != 0) { if ((request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_DATA_OUT) { CFISCSI_SESSION_WARN(cs, "got the final packet, but the " "transmitted size was %zd bytes instead of %d; " "dropping connection", (size_t)io->scsiio.ext_data_filled, cdw->cdw_r2t_end); ctl_set_data_phase_error(&io->scsiio); cfiscsi_session_terminate(cs); return (true); } else { /* * For SCSI Command PDU, this just means we need to * solicit more data by sending R2T. */ return (false); } } if (io->scsiio.ext_data_filled == cdw->cdw_r2t_end) { #if 0 CFISCSI_SESSION_DEBUG(cs, "no longer expecting Data-Out with target " "transfer tag 0x%x", cdw->cdw_target_transfer_tag); #endif return (true); } return (false); } static void cfiscsi_pdu_handle_data_out(struct icl_pdu *request) { struct iscsi_bhs_data_out *bhsdo; struct cfiscsi_session *cs; struct cfiscsi_data_wait *cdw = NULL; union ctl_io *io; bool done; cs = PDU_SESSION(request); bhsdo = (struct iscsi_bhs_data_out *)request->ip_bhs; CFISCSI_SESSION_LOCK(cs); TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) { #if 0 CFISCSI_SESSION_DEBUG(cs, "have ttt 0x%x, itt 0x%x; looking for " "ttt 0x%x, itt 0x%x", bhsdo->bhsdo_target_transfer_tag, bhsdo->bhsdo_initiator_task_tag, cdw->cdw_target_transfer_tag, cdw->cdw_initiator_task_tag)); #endif if (bhsdo->bhsdo_target_transfer_tag == cdw->cdw_target_transfer_tag) break; } CFISCSI_SESSION_UNLOCK(cs); if (cdw == NULL) { CFISCSI_SESSION_WARN(cs, "data transfer tag 0x%x, initiator task tag " "0x%x, not found; dropping connection", bhsdo->bhsdo_target_transfer_tag, bhsdo->bhsdo_initiator_task_tag); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } if (cdw->cdw_datasn != ntohl(bhsdo->bhsdo_datasn)) { CFISCSI_SESSION_WARN(cs, "received Data-Out PDU with " "DataSN %u, while expected %u; dropping connection", ntohl(bhsdo->bhsdo_datasn), cdw->cdw_datasn); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } cdw->cdw_datasn++; io = cdw->cdw_ctl_io; KASSERT((io->io_hdr.flags & CTL_FLAG_DATA_MASK) != CTL_FLAG_DATA_IN, ("CTL_FLAG_DATA_IN")); done = cfiscsi_handle_data_segment(request, cdw); if (done) { CFISCSI_SESSION_LOCK(cs); TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next); CFISCSI_SESSION_UNLOCK(cs); done = (io->scsiio.ext_data_filled != cdw->cdw_r2t_end || io->scsiio.ext_data_filled == io->scsiio.kern_data_len); uma_zfree(cfiscsi_data_wait_zone, cdw); if (done) io->scsiio.be_move_done(io); else cfiscsi_datamove_out(io); } icl_pdu_free(request); } static void cfiscsi_pdu_handle_logout_request(struct icl_pdu *request) { struct iscsi_bhs_logout_request *bhslr; struct iscsi_bhs_logout_response *bhslr2; struct icl_pdu *response; struct cfiscsi_session *cs; cs = PDU_SESSION(request); bhslr = (struct iscsi_bhs_logout_request *)request->ip_bhs; switch (bhslr->bhslr_reason & 0x7f) { case BHSLR_REASON_CLOSE_SESSION: case BHSLR_REASON_CLOSE_CONNECTION: response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_DEBUG(cs, "failed to allocate memory"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs; bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; bhslr2->bhslr_flags = 0x80; bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; icl_pdu_free(request); cfiscsi_pdu_queue(response); cfiscsi_session_terminate(cs); break; case BHSLR_REASON_REMOVE_FOR_RECOVERY: response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory; dropping connection"); icl_pdu_free(request); cfiscsi_session_terminate(cs); return; } bhslr2 = (struct iscsi_bhs_logout_response *)response->ip_bhs; bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; bhslr2->bhslr_flags = 0x80; bhslr2->bhslr_response = BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED; bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; icl_pdu_free(request); cfiscsi_pdu_queue(response); break; default: CFISCSI_SESSION_WARN(cs, "invalid reason 0%x; dropping connection", bhslr->bhslr_reason); icl_pdu_free(request); cfiscsi_session_terminate(cs); break; } } static void cfiscsi_callout(void *context) { struct icl_pdu *cp; struct iscsi_bhs_nop_in *bhsni; struct cfiscsi_session *cs; cs = context; if (cs->cs_terminating) return; callout_schedule(&cs->cs_callout, 1 * hz); atomic_add_int(&cs->cs_timeout, 1); #ifdef ICL_KERNEL_PROXY if (cs->cs_waiting_for_ctld || cs->cs_login_phase) { if (login_timeout > 0 && cs->cs_timeout > login_timeout) { CFISCSI_SESSION_WARN(cs, "login timed out after " "%d seconds; dropping connection", cs->cs_timeout); cfiscsi_session_terminate(cs); } return; } #endif if (ping_timeout <= 0) { /* * Pings are disabled. Don't send NOP-In in this case; * user might have disabled pings to work around problems * with certain initiators that can't properly handle * NOP-In, such as iPXE. Reset the timeout, to avoid * triggering reconnection, should the user decide to * reenable them. */ cs->cs_timeout = 0; return; } if (cs->cs_timeout >= ping_timeout) { CFISCSI_SESSION_WARN(cs, "no ping reply (NOP-Out) after %d seconds; " "dropping connection", ping_timeout); cfiscsi_session_terminate(cs); return; } /* * If the ping was reset less than one second ago - which means * that we've received some PDU during the last second - assume * the traffic flows correctly and don't bother sending a NOP-Out. * * (It's 2 - one for one second, and one for incrementing is_timeout * earlier in this routine.) */ if (cs->cs_timeout < 2) return; cp = icl_pdu_new(cs->cs_conn, M_NOWAIT); if (cp == NULL) { CFISCSI_SESSION_WARN(cs, "failed to allocate memory"); return; } bhsni = (struct iscsi_bhs_nop_in *)cp->ip_bhs; bhsni->bhsni_opcode = ISCSI_BHS_OPCODE_NOP_IN; bhsni->bhsni_flags = 0x80; bhsni->bhsni_initiator_task_tag = 0xffffffff; cfiscsi_pdu_queue(cp); } static void cfiscsi_session_terminate_tasks(struct cfiscsi_session *cs) { struct cfiscsi_data_wait *cdw; union ctl_io *io; int error, last, wait; if (cs->cs_target == NULL) return; /* No target yet, so nothing to do. */ io = ctl_alloc_io(cs->cs_target->ct_port.ctl_pool_ref); ctl_zero_io(io); io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = cs; io->io_hdr.io_type = CTL_IO_TASK; io->io_hdr.nexus.initid.id = cs->cs_ctl_initid; io->io_hdr.nexus.targ_port = cs->cs_target->ct_port.targ_port; io->io_hdr.nexus.targ_target.id = 0; io->io_hdr.nexus.targ_lun = 0; io->taskio.tag_type = CTL_TAG_SIMPLE; /* XXX */ io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET; wait = cs->cs_outstanding_ctl_pdus; refcount_acquire(&cs->cs_outstanding_ctl_pdus); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { CFISCSI_SESSION_WARN(cs, "ctl_queue() failed; error %d", error); refcount_release(&cs->cs_outstanding_ctl_pdus); ctl_free_io(io); } CFISCSI_SESSION_LOCK(cs); while ((cdw = TAILQ_FIRST(&cs->cs_waiting_for_data_out)) != NULL) { TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next); CFISCSI_SESSION_UNLOCK(cs); /* * Set nonzero port status; this prevents backends from * assuming that the data transfer actually succeeded * and writing uninitialized data to disk. */ cdw->cdw_ctl_io->scsiio.io_hdr.port_status = 42; cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io); uma_zfree(cfiscsi_data_wait_zone, cdw); CFISCSI_SESSION_LOCK(cs); } CFISCSI_SESSION_UNLOCK(cs); /* * Wait for CTL to terminate all the tasks. */ if (wait > 0) CFISCSI_SESSION_WARN(cs, "waiting for CTL to terminate %d tasks", wait); for (;;) { refcount_acquire(&cs->cs_outstanding_ctl_pdus); last = refcount_release(&cs->cs_outstanding_ctl_pdus); if (last != 0) break; tsleep(__DEVOLATILE(void *, &cs->cs_outstanding_ctl_pdus), 0, "cfiscsi_terminate", hz / 100); } if (wait > 0) CFISCSI_SESSION_WARN(cs, "tasks terminated"); } static void cfiscsi_maintenance_thread(void *arg) { struct cfiscsi_session *cs; cs = arg; for (;;) { CFISCSI_SESSION_LOCK(cs); if (cs->cs_terminating == false) cv_wait(&cs->cs_maintenance_cv, &cs->cs_lock); CFISCSI_SESSION_UNLOCK(cs); if (cs->cs_terminating) { /* * We used to wait up to 30 seconds to deliver queued * PDUs to the initiator. We also tried hard to deliver * SCSI Responses for the aborted PDUs. We don't do * that anymore. We might need to revisit that. */ callout_drain(&cs->cs_callout); icl_conn_close(cs->cs_conn); /* * At this point ICL receive thread is no longer * running; no new tasks can be queued. */ cfiscsi_session_terminate_tasks(cs); cfiscsi_session_delete(cs); kthread_exit(); return; } CFISCSI_SESSION_DEBUG(cs, "nothing to do"); } } static void cfiscsi_session_terminate(struct cfiscsi_session *cs) { if (cs->cs_terminating) return; cs->cs_terminating = true; cv_signal(&cs->cs_maintenance_cv); #ifdef ICL_KERNEL_PROXY cv_signal(&cs->cs_login_cv); #endif } static int cfiscsi_session_register_initiator(struct cfiscsi_session *cs) { struct cfiscsi_target *ct; char *name; int i; KASSERT(cs->cs_ctl_initid == -1, ("already registered")); ct = cs->cs_target; name = strdup(cs->cs_initiator_id, M_CTL); i = ctl_add_initiator(&ct->ct_port, -1, 0, name); if (i < 0) { CFISCSI_SESSION_WARN(cs, "ctl_add_initiator failed with error %d", i); cs->cs_ctl_initid = -1; return (1); } cs->cs_ctl_initid = i; #if 0 CFISCSI_SESSION_DEBUG(cs, "added initiator id %d", i); #endif return (0); } static void cfiscsi_session_unregister_initiator(struct cfiscsi_session *cs) { int error; if (cs->cs_ctl_initid == -1) return; error = ctl_remove_initiator(&cs->cs_target->ct_port, cs->cs_ctl_initid); if (error != 0) { CFISCSI_SESSION_WARN(cs, "ctl_remove_initiator failed with error %d", error); } cs->cs_ctl_initid = -1; } static struct cfiscsi_session * cfiscsi_session_new(struct cfiscsi_softc *softc) { struct cfiscsi_session *cs; int error; cs = malloc(sizeof(*cs), M_CFISCSI, M_NOWAIT | M_ZERO); if (cs == NULL) { CFISCSI_WARN("malloc failed"); return (NULL); } cs->cs_ctl_initid = -1; refcount_init(&cs->cs_outstanding_ctl_pdus, 0); TAILQ_INIT(&cs->cs_waiting_for_data_out); mtx_init(&cs->cs_lock, "cfiscsi_lock", NULL, MTX_DEF); cv_init(&cs->cs_maintenance_cv, "cfiscsi_mt"); #ifdef ICL_KERNEL_PROXY cv_init(&cs->cs_login_cv, "cfiscsi_login"); #endif cs->cs_conn = icl_new_conn(NULL, "cfiscsi", &cs->cs_lock); cs->cs_conn->ic_receive = cfiscsi_receive_callback; cs->cs_conn->ic_error = cfiscsi_error_callback; cs->cs_conn->ic_prv0 = cs; error = kthread_add(cfiscsi_maintenance_thread, cs, NULL, NULL, 0, 0, "cfiscsimt"); if (error != 0) { CFISCSI_SESSION_WARN(cs, "kthread_add(9) failed with error %d", error); free(cs, M_CFISCSI); return (NULL); } mtx_lock(&softc->lock); cs->cs_id = ++softc->last_session_id; TAILQ_INSERT_TAIL(&softc->sessions, cs, cs_next); mtx_unlock(&softc->lock); /* * Start pinging the initiator. */ callout_init(&cs->cs_callout, 1); callout_reset(&cs->cs_callout, 1 * hz, cfiscsi_callout, cs); return (cs); } static void cfiscsi_session_delete(struct cfiscsi_session *cs) { struct cfiscsi_softc *softc; softc = &cfiscsi_softc; KASSERT(cs->cs_outstanding_ctl_pdus == 0, ("destroying session with outstanding CTL pdus")); KASSERT(TAILQ_EMPTY(&cs->cs_waiting_for_data_out), ("destroying session with non-empty queue")); cfiscsi_session_unregister_initiator(cs); if (cs->cs_target != NULL) cfiscsi_target_release(cs->cs_target); icl_conn_close(cs->cs_conn); icl_conn_free(cs->cs_conn); mtx_lock(&softc->lock); TAILQ_REMOVE(&softc->sessions, cs, cs_next); cv_signal(&softc->sessions_cv); mtx_unlock(&softc->lock); free(cs, M_CFISCSI); } int cfiscsi_init(void) { struct cfiscsi_softc *softc; int retval; softc = &cfiscsi_softc; retval = 0; bzero(softc, sizeof(*softc)); mtx_init(&softc->lock, "cfiscsi", NULL, MTX_DEF); cv_init(&softc->sessions_cv, "cfiscsi_sessions"); #ifdef ICL_KERNEL_PROXY cv_init(&softc->accept_cv, "cfiscsi_accept"); #endif TAILQ_INIT(&softc->sessions); TAILQ_INIT(&softc->targets); cfiscsi_data_wait_zone = uma_zcreate("cfiscsi_data_wait", sizeof(struct cfiscsi_data_wait), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); return (0); } #ifdef ICL_KERNEL_PROXY static void cfiscsi_accept(struct socket *so, struct sockaddr *sa, int portal_id) { struct cfiscsi_session *cs; cs = cfiscsi_session_new(&cfiscsi_softc); if (cs == NULL) { CFISCSI_WARN("failed to create session"); return; } icl_conn_handoff_sock(cs->cs_conn, so); cs->cs_initiator_sa = sa; cs->cs_portal_id = portal_id; cs->cs_waiting_for_ctld = true; cv_signal(&cfiscsi_softc.accept_cv); } #endif static void cfiscsi_online(void *arg) { struct cfiscsi_softc *softc; struct cfiscsi_target *ct; int online; ct = (struct cfiscsi_target *)arg; softc = ct->ct_softc; mtx_lock(&softc->lock); if (ct->ct_online) { mtx_unlock(&softc->lock); return; } ct->ct_online = 1; online = softc->online++; mtx_unlock(&softc->lock); if (online > 0) return; #ifdef ICL_KERNEL_PROXY if (softc->listener != NULL) icl_listen_free(softc->listener); softc->listener = icl_listen_new(cfiscsi_accept); #endif } static void cfiscsi_offline(void *arg) { struct cfiscsi_softc *softc; struct cfiscsi_target *ct; struct cfiscsi_session *cs; int online; ct = (struct cfiscsi_target *)arg; softc = ct->ct_softc; mtx_lock(&softc->lock); if (!ct->ct_online) { mtx_unlock(&softc->lock); return; } ct->ct_online = 0; online = --softc->online; TAILQ_FOREACH(cs, &softc->sessions, cs_next) { if (cs->cs_target == ct) cfiscsi_session_terminate(cs); } do { TAILQ_FOREACH(cs, &softc->sessions, cs_next) { if (cs->cs_target == ct) break; } if (cs != NULL) cv_wait(&softc->sessions_cv, &softc->lock); } while (cs != NULL && ct->ct_online == 0); mtx_unlock(&softc->lock); if (online > 0) return; #ifdef ICL_KERNEL_PROXY icl_listen_free(softc->listener); softc->listener = NULL; #endif } static int cfiscsi_info(void *arg, struct sbuf *sb) { struct cfiscsi_target *ct = (struct cfiscsi_target *)arg; int retval; retval = sbuf_printf(sb, "\t%d\n", ct->ct_state); return (retval); } static void cfiscsi_ioctl_handoff(struct ctl_iscsi *ci) { struct cfiscsi_softc *softc; struct cfiscsi_session *cs, *cs2; struct cfiscsi_target *ct; struct ctl_iscsi_handoff_params *cihp; int error; cihp = (struct ctl_iscsi_handoff_params *)&(ci->data); softc = &cfiscsi_softc; CFISCSI_DEBUG("new connection from %s (%s) to %s", cihp->initiator_name, cihp->initiator_addr, cihp->target_name); - ct = cfiscsi_target_find(softc, cihp->target_name); + ct = cfiscsi_target_find(softc, cihp->target_name, + cihp->portal_group_tag); if (ct == NULL) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: target not found", __func__); return; } #ifdef ICL_KERNEL_PROXY if (cihp->socket > 0 && cihp->connection_id > 0) { snprintf(ci->error_str, sizeof(ci->error_str), "both socket and connection_id set"); ci->status = CTL_ISCSI_ERROR; cfiscsi_target_release(ct); return; } if (cihp->socket == 0) { mtx_lock(&cfiscsi_softc.lock); TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { if (cs->cs_id == cihp->connection_id) break; } if (cs == NULL) { mtx_unlock(&cfiscsi_softc.lock); snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); ci->status = CTL_ISCSI_ERROR; cfiscsi_target_release(ct); return; } mtx_unlock(&cfiscsi_softc.lock); } else { #endif cs = cfiscsi_session_new(softc); if (cs == NULL) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: cfiscsi_session_new failed", __func__); cfiscsi_target_release(ct); return; } #ifdef ICL_KERNEL_PROXY } #endif /* * First PDU of Full Feature phase has the same CmdSN as the last * PDU from the Login Phase received from the initiator. Thus, * the -1 below. */ - cs->cs_portal_group_tag = cihp->portal_group_tag; cs->cs_cmdsn = cihp->cmdsn; cs->cs_statsn = cihp->statsn; cs->cs_max_data_segment_length = cihp->max_recv_data_segment_length; cs->cs_max_burst_length = cihp->max_burst_length; cs->cs_immediate_data = !!cihp->immediate_data; if (cihp->header_digest == CTL_ISCSI_DIGEST_CRC32C) cs->cs_conn->ic_header_crc32c = true; if (cihp->data_digest == CTL_ISCSI_DIGEST_CRC32C) cs->cs_conn->ic_data_crc32c = true; strlcpy(cs->cs_initiator_name, cihp->initiator_name, sizeof(cs->cs_initiator_name)); strlcpy(cs->cs_initiator_addr, cihp->initiator_addr, sizeof(cs->cs_initiator_addr)); strlcpy(cs->cs_initiator_alias, cihp->initiator_alias, sizeof(cs->cs_initiator_alias)); memcpy(cs->cs_initiator_isid, cihp->initiator_isid, sizeof(cs->cs_initiator_isid)); snprintf(cs->cs_initiator_id, sizeof(cs->cs_initiator_id), "%s,i,0x%02x%02x%02x%02x%02x%02x", cs->cs_initiator_name, cihp->initiator_isid[0], cihp->initiator_isid[1], cihp->initiator_isid[2], cihp->initiator_isid[3], cihp->initiator_isid[4], cihp->initiator_isid[5]); mtx_lock(&softc->lock); if (ct->ct_online == 0) { mtx_unlock(&softc->lock); cfiscsi_session_terminate(cs); cfiscsi_target_release(ct); ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: port offline", __func__); return; } cs->cs_target = ct; mtx_unlock(&softc->lock); refcount_acquire(&cs->cs_outstanding_ctl_pdus); restart: if (!cs->cs_terminating) { mtx_lock(&softc->lock); TAILQ_FOREACH(cs2, &softc->sessions, cs_next) { if (cs2 != cs && cs2->cs_tasks_aborted == false && cs->cs_target == cs2->cs_target && - cs->cs_portal_group_tag == cs2->cs_portal_group_tag && strcmp(cs->cs_initiator_id, cs2->cs_initiator_id) == 0) { cfiscsi_session_terminate(cs2); mtx_unlock(&softc->lock); pause("cfiscsi_reinstate", 1); goto restart; } } mtx_unlock(&softc->lock); } /* * Register initiator with CTL. */ cfiscsi_session_register_initiator(cs); #ifdef ICL_KERNEL_PROXY if (cihp->socket > 0) { #endif error = icl_conn_handoff(cs->cs_conn, cihp->socket); if (error != 0) { cfiscsi_session_terminate(cs); refcount_release(&cs->cs_outstanding_ctl_pdus); ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: icl_conn_handoff failed with error %d", __func__, error); return; } #ifdef ICL_KERNEL_PROXY } #endif #ifdef ICL_KERNEL_PROXY cs->cs_login_phase = false; /* * First PDU of the Full Feature phase has likely already arrived. * We have to pick it up and execute properly. */ if (cs->cs_login_pdu != NULL) { CFISCSI_SESSION_DEBUG(cs, "picking up first PDU"); cfiscsi_pdu_handle(cs->cs_login_pdu); cs->cs_login_pdu = NULL; } #endif refcount_release(&cs->cs_outstanding_ctl_pdus); ci->status = CTL_ISCSI_OK; } static void cfiscsi_ioctl_list(struct ctl_iscsi *ci) { struct ctl_iscsi_list_params *cilp; struct cfiscsi_session *cs; struct cfiscsi_softc *softc; struct sbuf *sb; int error; cilp = (struct ctl_iscsi_list_params *)&(ci->data); softc = &cfiscsi_softc; sb = sbuf_new(NULL, NULL, cilp->alloc_len, SBUF_FIXEDLEN); if (sb == NULL) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "Unable to allocate %d bytes for iSCSI session list", cilp->alloc_len); return; } sbuf_printf(sb, "\n"); mtx_lock(&softc->lock); TAILQ_FOREACH(cs, &softc->sessions, cs_next) { #ifdef ICL_KERNEL_PROXY if (cs->cs_target == NULL) continue; #endif error = sbuf_printf(sb, "" "%s" "%s" "%s" "%s" "%s" + "%u" "%s" "%s" "%zd" "%d" "%d" "\n", cs->cs_id, cs->cs_initiator_name, cs->cs_initiator_addr, cs->cs_initiator_alias, cs->cs_target->ct_name, cs->cs_target->ct_alias, + cs->cs_target->ct_tag, cs->cs_conn->ic_header_crc32c ? "CRC32C" : "None", cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None", cs->cs_max_data_segment_length, cs->cs_immediate_data, cs->cs_conn->ic_iser); if (error != 0) break; } mtx_unlock(&softc->lock); error = sbuf_printf(sb, "\n"); if (error != 0) { sbuf_delete(sb); ci->status = CTL_ISCSI_LIST_NEED_MORE_SPACE; snprintf(ci->error_str, sizeof(ci->error_str), "Out of space, %d bytes is too small", cilp->alloc_len); return; } sbuf_finish(sb); error = copyout(sbuf_data(sb), cilp->conn_xml, sbuf_len(sb) + 1); cilp->fill_len = sbuf_len(sb) + 1; ci->status = CTL_ISCSI_OK; sbuf_delete(sb); } static void cfiscsi_ioctl_terminate(struct ctl_iscsi *ci) { struct icl_pdu *response; struct iscsi_bhs_asynchronous_message *bhsam; struct ctl_iscsi_terminate_params *citp; struct cfiscsi_session *cs; struct cfiscsi_softc *softc; int found = 0; citp = (struct ctl_iscsi_terminate_params *)&(ci->data); softc = &cfiscsi_softc; mtx_lock(&softc->lock); TAILQ_FOREACH(cs, &softc->sessions, cs_next) { if (citp->all == 0 && cs->cs_id != citp->connection_id && strcmp(cs->cs_initiator_name, citp->initiator_name) != 0 && strcmp(cs->cs_initiator_addr, citp->initiator_addr) != 0) continue; response = icl_pdu_new(cs->cs_conn, M_NOWAIT); if (response == NULL) { /* * Oh well. Just terminate the connection. */ } else { bhsam = (struct iscsi_bhs_asynchronous_message *) response->ip_bhs; bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE; bhsam->bhsam_flags = 0x80; bhsam->bhsam_0xffffffff = 0xffffffff; bhsam->bhsam_async_event = BHSAM_EVENT_TARGET_TERMINATES_SESSION; cfiscsi_pdu_queue(response); } cfiscsi_session_terminate(cs); found++; } mtx_unlock(&softc->lock); if (found == 0) { ci->status = CTL_ISCSI_SESSION_NOT_FOUND; snprintf(ci->error_str, sizeof(ci->error_str), "No matching connections found"); return; } ci->status = CTL_ISCSI_OK; } static void cfiscsi_ioctl_logout(struct ctl_iscsi *ci) { struct icl_pdu *response; struct iscsi_bhs_asynchronous_message *bhsam; struct ctl_iscsi_logout_params *cilp; struct cfiscsi_session *cs; struct cfiscsi_softc *softc; int found = 0; cilp = (struct ctl_iscsi_logout_params *)&(ci->data); softc = &cfiscsi_softc; mtx_lock(&softc->lock); TAILQ_FOREACH(cs, &softc->sessions, cs_next) { if (cilp->all == 0 && cs->cs_id != cilp->connection_id && strcmp(cs->cs_initiator_name, cilp->initiator_name) != 0 && strcmp(cs->cs_initiator_addr, cilp->initiator_addr) != 0) continue; response = icl_pdu_new(cs->cs_conn, M_NOWAIT); if (response == NULL) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "Unable to allocate memory"); mtx_unlock(&softc->lock); return; } bhsam = (struct iscsi_bhs_asynchronous_message *)response->ip_bhs; bhsam->bhsam_opcode = ISCSI_BHS_OPCODE_ASYNC_MESSAGE; bhsam->bhsam_flags = 0x80; bhsam->bhsam_async_event = BHSAM_EVENT_TARGET_REQUESTS_LOGOUT; bhsam->bhsam_parameter3 = htons(10); cfiscsi_pdu_queue(response); found++; } mtx_unlock(&softc->lock); if (found == 0) { ci->status = CTL_ISCSI_SESSION_NOT_FOUND; snprintf(ci->error_str, sizeof(ci->error_str), "No matching connections found"); return; } ci->status = CTL_ISCSI_OK; } #ifdef ICL_KERNEL_PROXY static void cfiscsi_ioctl_listen(struct ctl_iscsi *ci) { struct ctl_iscsi_listen_params *cilp; struct sockaddr *sa; int error; cilp = (struct ctl_iscsi_listen_params *)&(ci->data); if (cfiscsi_softc.listener == NULL) { CFISCSI_DEBUG("no listener"); snprintf(ci->error_str, sizeof(ci->error_str), "no listener"); ci->status = CTL_ISCSI_ERROR; return; } error = getsockaddr(&sa, (void *)cilp->addr, cilp->addrlen); if (error != 0) { CFISCSI_DEBUG("getsockaddr, error %d", error); snprintf(ci->error_str, sizeof(ci->error_str), "getsockaddr failed"); ci->status = CTL_ISCSI_ERROR; return; } error = icl_listen_add(cfiscsi_softc.listener, cilp->iser, cilp->domain, cilp->socktype, cilp->protocol, sa, cilp->portal_id); if (error != 0) { free(sa, M_SONAME); CFISCSI_DEBUG("icl_listen_add, error %d", error); snprintf(ci->error_str, sizeof(ci->error_str), "icl_listen_add failed, error %d", error); ci->status = CTL_ISCSI_ERROR; return; } ci->status = CTL_ISCSI_OK; } static void cfiscsi_ioctl_accept(struct ctl_iscsi *ci) { struct ctl_iscsi_accept_params *ciap; struct cfiscsi_session *cs; int error; ciap = (struct ctl_iscsi_accept_params *)&(ci->data); mtx_lock(&cfiscsi_softc.lock); for (;;) { TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { if (cs->cs_waiting_for_ctld) break; } if (cs != NULL) break; error = cv_wait_sig(&cfiscsi_softc.accept_cv, &cfiscsi_softc.lock); if (error != 0) { mtx_unlock(&cfiscsi_softc.lock); snprintf(ci->error_str, sizeof(ci->error_str), "interrupted"); ci->status = CTL_ISCSI_ERROR; return; } } mtx_unlock(&cfiscsi_softc.lock); cs->cs_waiting_for_ctld = false; cs->cs_login_phase = true; ciap->connection_id = cs->cs_id; ciap->portal_id = cs->cs_portal_id; ciap->initiator_addrlen = cs->cs_initiator_sa->sa_len; error = copyout(cs->cs_initiator_sa, ciap->initiator_addr, cs->cs_initiator_sa->sa_len); if (error != 0) { snprintf(ci->error_str, sizeof(ci->error_str), "copyout failed with error %d", error); ci->status = CTL_ISCSI_ERROR; return; } ci->status = CTL_ISCSI_OK; } static void cfiscsi_ioctl_send(struct ctl_iscsi *ci) { struct ctl_iscsi_send_params *cisp; struct cfiscsi_session *cs; struct icl_pdu *ip; size_t datalen; void *data; int error; cisp = (struct ctl_iscsi_send_params *)&(ci->data); mtx_lock(&cfiscsi_softc.lock); TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { if (cs->cs_id == cisp->connection_id) break; } if (cs == NULL) { mtx_unlock(&cfiscsi_softc.lock); snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); ci->status = CTL_ISCSI_ERROR; return; } mtx_unlock(&cfiscsi_softc.lock); #if 0 if (cs->cs_login_phase == false) return (EBUSY); #endif if (cs->cs_terminating) { snprintf(ci->error_str, sizeof(ci->error_str), "connection is terminating"); ci->status = CTL_ISCSI_ERROR; return; } datalen = cisp->data_segment_len; /* * XXX */ //if (datalen > CFISCSI_MAX_DATA_SEGMENT_LENGTH) { if (datalen > 65535) { snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big"); ci->status = CTL_ISCSI_ERROR; return; } if (datalen > 0) { data = malloc(datalen, M_CFISCSI, M_WAITOK); error = copyin(cisp->data_segment, data, datalen); if (error != 0) { free(data, M_CFISCSI); snprintf(ci->error_str, sizeof(ci->error_str), "copyin error %d", error); ci->status = CTL_ISCSI_ERROR; return; } } ip = icl_pdu_new(cs->cs_conn, M_WAITOK); memcpy(ip->ip_bhs, cisp->bhs, sizeof(*ip->ip_bhs)); if (datalen > 0) { icl_pdu_append_data(ip, data, datalen, M_WAITOK); free(data, M_CFISCSI); } CFISCSI_SESSION_LOCK(cs); icl_pdu_queue(ip); CFISCSI_SESSION_UNLOCK(cs); ci->status = CTL_ISCSI_OK; } static void cfiscsi_ioctl_receive(struct ctl_iscsi *ci) { struct ctl_iscsi_receive_params *cirp; struct cfiscsi_session *cs; struct icl_pdu *ip; void *data; int error; cirp = (struct ctl_iscsi_receive_params *)&(ci->data); mtx_lock(&cfiscsi_softc.lock); TAILQ_FOREACH(cs, &cfiscsi_softc.sessions, cs_next) { if (cs->cs_id == cirp->connection_id) break; } if (cs == NULL) { mtx_unlock(&cfiscsi_softc.lock); snprintf(ci->error_str, sizeof(ci->error_str), "connection not found"); ci->status = CTL_ISCSI_ERROR; return; } mtx_unlock(&cfiscsi_softc.lock); #if 0 if (is->is_login_phase == false) return (EBUSY); #endif CFISCSI_SESSION_LOCK(cs); while (cs->cs_login_pdu == NULL && cs->cs_terminating == false) { error = cv_wait_sig(&cs->cs_login_cv, &cs->cs_lock); if (error != 0) { CFISCSI_SESSION_UNLOCK(cs); snprintf(ci->error_str, sizeof(ci->error_str), "interrupted by signal"); ci->status = CTL_ISCSI_ERROR; return; } } if (cs->cs_terminating) { CFISCSI_SESSION_UNLOCK(cs); snprintf(ci->error_str, sizeof(ci->error_str), "connection terminating"); ci->status = CTL_ISCSI_ERROR; return; } ip = cs->cs_login_pdu; cs->cs_login_pdu = NULL; CFISCSI_SESSION_UNLOCK(cs); if (ip->ip_data_len > cirp->data_segment_len) { icl_pdu_free(ip); snprintf(ci->error_str, sizeof(ci->error_str), "data segment too big"); ci->status = CTL_ISCSI_ERROR; return; } copyout(ip->ip_bhs, cirp->bhs, sizeof(*ip->ip_bhs)); if (ip->ip_data_len > 0) { data = malloc(ip->ip_data_len, M_CFISCSI, M_WAITOK); icl_pdu_get_data(ip, 0, data, ip->ip_data_len); copyout(data, cirp->data_segment, ip->ip_data_len); free(data, M_CFISCSI); } icl_pdu_free(ip); ci->status = CTL_ISCSI_OK; } #endif /* !ICL_KERNEL_PROXY */ static void cfiscsi_ioctl_port_create(struct ctl_req *req) { struct cfiscsi_target *ct; struct ctl_port *port; - const char *target, *alias, *tag; + const char *target, *alias, *tags; struct scsi_vpd_id_descriptor *desc; ctl_options_t opts; int retval, len, idlen; + uint16_t tag; ctl_init_opts(&opts, req->num_args, req->kern_args); target = ctl_get_opt(&opts, "cfiscsi_target"); alias = ctl_get_opt(&opts, "cfiscsi_target_alias"); - tag = ctl_get_opt(&opts, "cfiscsi_portal_group_tag"); - if (target == NULL || tag == NULL) { + tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag"); + if (target == NULL || tags == NULL) { req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "Missing required argument"); ctl_free_opts(&opts); return; } - ct = cfiscsi_target_find_or_create(&cfiscsi_softc, target, alias); + tag = strtol(tags, (char **)NULL, 10); + ct = cfiscsi_target_find_or_create(&cfiscsi_softc, target, alias, tag); if (ct == NULL) { req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "failed to create target \"%s\"", target); ctl_free_opts(&opts); return; } if (ct->ct_state == CFISCSI_TARGET_STATE_ACTIVE) { req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "target \"%s\" already exists", target); cfiscsi_target_release(ct); ctl_free_opts(&opts); return; } port = &ct->ct_port; // WAT if (ct->ct_state == CFISCSI_TARGET_STATE_DYING) goto done; port->frontend = &cfiscsi_frontend; port->port_type = CTL_PORT_ISCSI; /* XXX KDM what should the real number be here? */ port->num_requested_ctl_io = 4096; port->port_name = "iscsi"; - port->physical_port = strtoul(tag, NULL, 0); + port->physical_port = tag; port->virtual_port = ct->ct_target_id; port->port_online = cfiscsi_online; port->port_offline = cfiscsi_offline; port->port_info = cfiscsi_info; port->onoff_arg = ct; port->lun_enable = cfiscsi_lun_enable; port->lun_disable = cfiscsi_lun_disable; port->targ_lun_arg = ct; port->fe_datamove = cfiscsi_datamove; port->fe_done = cfiscsi_done; /* XXX KDM what should we report here? */ /* XXX These should probably be fetched from CTL. */ port->max_targets = 1; port->max_target_id = 15; port->options = opts; STAILQ_INIT(&opts); /* Generate Port ID. */ idlen = strlen(target) + strlen(",t,0x0001") + 1; idlen = roundup2(idlen, 4); len = sizeof(struct scsi_vpd_device_id) + idlen; port->port_devid = malloc(sizeof(struct ctl_devid) + len, M_CTL, M_WAITOK | M_ZERO); port->port_devid->len = len; desc = (struct scsi_vpd_id_descriptor *)port->port_devid->data; desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8; desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_PORT | SVPD_ID_TYPE_SCSI_NAME; desc->length = idlen; - snprintf(desc->identifier, idlen, "%s,t,0x%4.4x", - target, port->physical_port); + snprintf(desc->identifier, idlen, "%s,t,0x%4.4x", target, tag); /* Generate Target ID. */ idlen = strlen(target) + 1; idlen = roundup2(idlen, 4); len = sizeof(struct scsi_vpd_device_id) + idlen; port->target_devid = malloc(sizeof(struct ctl_devid) + len, M_CTL, M_WAITOK | M_ZERO); port->target_devid->len = len; desc = (struct scsi_vpd_id_descriptor *)port->target_devid->data; desc->proto_codeset = (SCSI_PROTO_ISCSI << 4) | SVPD_ID_CODESET_UTF8; desc->id_type = SVPD_ID_PIV | SVPD_ID_ASSOC_TARGET | SVPD_ID_TYPE_SCSI_NAME; desc->length = idlen; strlcpy(desc->identifier, target, idlen); retval = ctl_port_register(port); if (retval != 0) { ctl_free_opts(&port->options); cfiscsi_target_release(ct); free(port->port_devid, M_CFISCSI); free(port->target_devid, M_CFISCSI); req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "ctl_port_register() failed with error %d", retval); return; } done: ct->ct_state = CFISCSI_TARGET_STATE_ACTIVE; req->status = CTL_LUN_OK; memcpy(req->kern_args[0].kvalue, &port->targ_port, sizeof(port->targ_port)); //XXX } static void cfiscsi_ioctl_port_remove(struct ctl_req *req) { struct cfiscsi_target *ct; - const char *target; + const char *target, *tags; ctl_options_t opts; + uint16_t tag; ctl_init_opts(&opts, req->num_args, req->kern_args); target = ctl_get_opt(&opts, "cfiscsi_target"); - if (target == NULL) { + tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag"); + if (target == NULL || tags == NULL) { ctl_free_opts(&opts); req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "Missing required argument"); return; } - ct = cfiscsi_target_find(&cfiscsi_softc, target); + tag = strtol(tags, (char **)NULL, 10); + ct = cfiscsi_target_find(&cfiscsi_softc, target, tag); if (ct == NULL) { ctl_free_opts(&opts); req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "can't find target \"%s\"", target); return; } if (ct->ct_state != CFISCSI_TARGET_STATE_ACTIVE) { ctl_free_opts(&opts); req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "target \"%s\" is already dying", target); return; } ctl_free_opts(&opts); ct->ct_state = CFISCSI_TARGET_STATE_DYING; ctl_port_offline(&ct->ct_port); cfiscsi_target_release(ct); cfiscsi_target_release(ct); + req->status = CTL_LUN_OK; } static int cfiscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct ctl_iscsi *ci; struct ctl_req *req; if (cmd == CTL_PORT_REQ) { req = (struct ctl_req *)addr; switch (req->reqtype) { case CTL_REQ_CREATE: cfiscsi_ioctl_port_create(req); break; case CTL_REQ_REMOVE: cfiscsi_ioctl_port_remove(req); break; default: req->status = CTL_LUN_ERROR; snprintf(req->error_str, sizeof(req->error_str), "Unsupported request type %d", req->reqtype); } return (0); } if (cmd != CTL_ISCSI) return (ENOTTY); ci = (struct ctl_iscsi *)addr; switch (ci->type) { case CTL_ISCSI_HANDOFF: cfiscsi_ioctl_handoff(ci); break; case CTL_ISCSI_LIST: cfiscsi_ioctl_list(ci); break; case CTL_ISCSI_TERMINATE: cfiscsi_ioctl_terminate(ci); break; case CTL_ISCSI_LOGOUT: cfiscsi_ioctl_logout(ci); break; #ifdef ICL_KERNEL_PROXY case CTL_ISCSI_LISTEN: cfiscsi_ioctl_listen(ci); break; case CTL_ISCSI_ACCEPT: cfiscsi_ioctl_accept(ci); break; case CTL_ISCSI_SEND: cfiscsi_ioctl_send(ci); break; case CTL_ISCSI_RECEIVE: cfiscsi_ioctl_receive(ci); break; #else case CTL_ISCSI_LISTEN: case CTL_ISCSI_ACCEPT: case CTL_ISCSI_SEND: case CTL_ISCSI_RECEIVE: ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: CTL compiled without ICL_KERNEL_PROXY", __func__); break; #endif /* !ICL_KERNEL_PROXY */ default: ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), "%s: invalid iSCSI request type %d", __func__, ci->type); break; } return (0); } static void cfiscsi_target_hold(struct cfiscsi_target *ct) { refcount_acquire(&ct->ct_refcount); } static void cfiscsi_target_release(struct cfiscsi_target *ct) { struct cfiscsi_softc *softc; softc = ct->ct_softc; mtx_lock(&softc->lock); if (refcount_release(&ct->ct_refcount)) { TAILQ_REMOVE(&softc->targets, ct, ct_next); mtx_unlock(&softc->lock); if (ct->ct_state != CFISCSI_TARGET_STATE_INVALID) { ct->ct_state = CFISCSI_TARGET_STATE_INVALID; if (ctl_port_deregister(&ct->ct_port) != 0) printf("%s: ctl_port_deregister() failed\n", __func__); } free(ct, M_CFISCSI); return; } mtx_unlock(&softc->lock); } static struct cfiscsi_target * -cfiscsi_target_find(struct cfiscsi_softc *softc, const char *name) +cfiscsi_target_find(struct cfiscsi_softc *softc, const char *name, uint16_t tag) { struct cfiscsi_target *ct; mtx_lock(&softc->lock); TAILQ_FOREACH(ct, &softc->targets, ct_next) { - if (strcmp(name, ct->ct_name) != 0 || + if (ct->ct_tag != tag || + strcmp(name, ct->ct_name) != 0 || ct->ct_state != CFISCSI_TARGET_STATE_ACTIVE) continue; cfiscsi_target_hold(ct); mtx_unlock(&softc->lock); return (ct); } mtx_unlock(&softc->lock); return (NULL); } static struct cfiscsi_target * cfiscsi_target_find_or_create(struct cfiscsi_softc *softc, const char *name, - const char *alias) + const char *alias, uint16_t tag) { struct cfiscsi_target *ct, *newct; if (name[0] == '\0' || strlen(name) >= CTL_ISCSI_NAME_LEN) return (NULL); newct = malloc(sizeof(*newct), M_CFISCSI, M_WAITOK | M_ZERO); mtx_lock(&softc->lock); TAILQ_FOREACH(ct, &softc->targets, ct_next) { - if (strcmp(name, ct->ct_name) != 0 || + if (ct->ct_tag != tag || + strcmp(name, ct->ct_name) != 0 || ct->ct_state == CFISCSI_TARGET_STATE_INVALID) continue; cfiscsi_target_hold(ct); mtx_unlock(&softc->lock); free(newct, M_CFISCSI); return (ct); } strlcpy(newct->ct_name, name, sizeof(newct->ct_name)); if (alias != NULL) strlcpy(newct->ct_alias, alias, sizeof(newct->ct_alias)); + newct->ct_tag = tag; refcount_init(&newct->ct_refcount, 1); newct->ct_softc = softc; if (TAILQ_EMPTY(&softc->targets)) softc->last_target_id = 0; newct->ct_target_id = ++softc->last_target_id; TAILQ_INSERT_TAIL(&softc->targets, newct, ct_next); mtx_unlock(&softc->lock); return (newct); } static int cfiscsi_lun_enable(void *arg, struct ctl_id target_id, int lun_id) { return (0); } static int cfiscsi_lun_disable(void *arg, struct ctl_id target_id, int lun_id) { return (0); } static void cfiscsi_datamove_in(union ctl_io *io) { struct cfiscsi_session *cs; struct icl_pdu *request, *response; const struct iscsi_bhs_scsi_command *bhssc; struct iscsi_bhs_data_in *bhsdi; struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; size_t len, expected_len, sg_len, buffer_offset; const char *sg_addr; int ctl_sg_count, error, i; request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs = PDU_SESSION(request); bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs; KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_COMMAND, ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND")); if (io->scsiio.kern_sg_entries > 0) { ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; ctl_sg_count = io->scsiio.kern_sg_entries; } else { ctl_sglist = &ctl_sg_entry; ctl_sglist->addr = io->scsiio.kern_data_ptr; ctl_sglist->len = io->scsiio.kern_data_len; ctl_sg_count = 1; } /* * This is the total amount of data to be transferred within the current * SCSI command. We need to record it so that we can properly report * underflow/underflow. */ PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len; /* * This is the offset within the current SCSI command; for the first * call to cfiscsi_datamove() it will be 0, and for subsequent ones * it will be the sum of lengths of previous ones. */ buffer_offset = io->scsiio.kern_rel_offset; /* * This is the transfer length expected by the initiator. In theory, * it could be different from the correct amount of data from the SCSI * point of view, even if that doesn't make any sense. */ expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length); #if 0 if (expected_len != io->scsiio.kern_total_len) { CFISCSI_SESSION_DEBUG(cs, "expected transfer length %zd, " "actual length %zd", expected_len, (size_t)io->scsiio.kern_total_len); } #endif if (buffer_offset >= expected_len) { #if 0 CFISCSI_SESSION_DEBUG(cs, "buffer_offset = %zd, " "already sent the expected len", buffer_offset); #endif io->scsiio.be_move_done(io); return; } i = 0; sg_addr = NULL; sg_len = 0; response = NULL; bhsdi = NULL; for (;;) { if (response == NULL) { response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_WARN(cs, "failed to " "allocate memory; dropping connection"); ctl_set_busy(&io->scsiio); io->scsiio.be_move_done(io); cfiscsi_session_terminate(cs); return; } bhsdi = (struct iscsi_bhs_data_in *)response->ip_bhs; bhsdi->bhsdi_opcode = ISCSI_BHS_OPCODE_SCSI_DATA_IN; bhsdi->bhsdi_initiator_task_tag = bhssc->bhssc_initiator_task_tag; bhsdi->bhsdi_datasn = htonl(PDU_EXPDATASN(request)); PDU_EXPDATASN(request)++; bhsdi->bhsdi_buffer_offset = htonl(buffer_offset); } KASSERT(i < ctl_sg_count, ("i >= ctl_sg_count")); if (sg_len == 0) { sg_addr = ctl_sglist[i].addr; sg_len = ctl_sglist[i].len; KASSERT(sg_len > 0, ("sg_len <= 0")); } len = sg_len; /* * Truncate to maximum data segment length. */ KASSERT(response->ip_data_len < cs->cs_max_data_segment_length, ("ip_data_len %zd >= max_data_segment_length %zd", response->ip_data_len, cs->cs_max_data_segment_length)); if (response->ip_data_len + len > cs->cs_max_data_segment_length) { len = cs->cs_max_data_segment_length - response->ip_data_len; KASSERT(len <= sg_len, ("len %zd > sg_len %zd", len, sg_len)); } /* * Truncate to expected data transfer length. */ KASSERT(buffer_offset + response->ip_data_len < expected_len, ("buffer_offset %zd + ip_data_len %zd >= expected_len %zd", buffer_offset, response->ip_data_len, expected_len)); if (buffer_offset + response->ip_data_len + len > expected_len) { CFISCSI_SESSION_DEBUG(cs, "truncating from %zd " "to expected data transfer length %zd", buffer_offset + response->ip_data_len + len, expected_len); len = expected_len - (buffer_offset + response->ip_data_len); KASSERT(len <= sg_len, ("len %zd > sg_len %zd", len, sg_len)); } error = icl_pdu_append_data(response, sg_addr, len, M_NOWAIT); if (error != 0) { CFISCSI_SESSION_WARN(cs, "failed to " "allocate memory; dropping connection"); icl_pdu_free(response); ctl_set_busy(&io->scsiio); io->scsiio.be_move_done(io); cfiscsi_session_terminate(cs); return; } sg_addr += len; sg_len -= len; KASSERT(buffer_offset + response->ip_data_len <= expected_len, ("buffer_offset %zd + ip_data_len %zd > expected_len %zd", buffer_offset, response->ip_data_len, expected_len)); if (buffer_offset + response->ip_data_len == expected_len) { /* * Already have the amount of data the initiator wanted. */ break; } if (sg_len == 0) { /* * End of scatter-gather segment; * proceed to the next one... */ if (i == ctl_sg_count - 1) { /* * ... unless this was the last one. */ break; } i++; } if (response->ip_data_len == cs->cs_max_data_segment_length) { /* * Can't stuff more data into the current PDU; * queue it. Note that's not enough to check * for kern_data_resid == 0 instead; there * may be several Data-In PDUs for the final * call to cfiscsi_datamove(), and we want * to set the F flag only on the last of them. */ buffer_offset += response->ip_data_len; if (buffer_offset == io->scsiio.kern_total_len || buffer_offset == expected_len) { buffer_offset -= response->ip_data_len; break; } cfiscsi_pdu_queue(response); response = NULL; bhsdi = NULL; } } if (response != NULL) { buffer_offset += response->ip_data_len; if (buffer_offset == io->scsiio.kern_total_len || buffer_offset == expected_len) { bhsdi->bhsdi_flags |= BHSDI_FLAGS_F; if (io->io_hdr.status == CTL_SUCCESS) { bhsdi->bhsdi_flags |= BHSDI_FLAGS_S; if (PDU_TOTAL_TRANSFER_LEN(request) < ntohl(bhssc->bhssc_expected_data_transfer_length)) { bhsdi->bhsdi_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW; bhsdi->bhsdi_residual_count = htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) - PDU_TOTAL_TRANSFER_LEN(request)); } else if (PDU_TOTAL_TRANSFER_LEN(request) > ntohl(bhssc->bhssc_expected_data_transfer_length)) { bhsdi->bhsdi_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW; bhsdi->bhsdi_residual_count = htonl(PDU_TOTAL_TRANSFER_LEN(request) - ntohl(bhssc->bhssc_expected_data_transfer_length)); } bhsdi->bhsdi_status = io->scsiio.scsi_status; io->io_hdr.flags |= CTL_FLAG_STATUS_SENT; } } KASSERT(response->ip_data_len > 0, ("sending empty Data-In")); cfiscsi_pdu_queue(response); } io->scsiio.be_move_done(io); } static void cfiscsi_datamove_out(union ctl_io *io) { struct cfiscsi_session *cs; struct icl_pdu *request, *response; const struct iscsi_bhs_scsi_command *bhssc; struct iscsi_bhs_r2t *bhsr2t; struct cfiscsi_data_wait *cdw; struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; uint32_t expected_len, r2t_off, r2t_len; uint32_t target_transfer_tag; bool done; request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs = PDU_SESSION(request); bhssc = (const struct iscsi_bhs_scsi_command *)request->ip_bhs; KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_COMMAND, ("bhssc->bhssc_opcode != ISCSI_BHS_OPCODE_SCSI_COMMAND")); /* * We need to record it so that we can properly report * underflow/underflow. */ PDU_TOTAL_TRANSFER_LEN(request) = io->scsiio.kern_total_len; /* * Report write underflow as error since CTL and backends don't * really support it, and SCSI does not tell how to do it right. */ expected_len = ntohl(bhssc->bhssc_expected_data_transfer_length); if (io->scsiio.kern_rel_offset + io->scsiio.kern_data_len > expected_len) { io->scsiio.io_hdr.port_status = 43; io->scsiio.be_move_done(io); return; } target_transfer_tag = atomic_fetchadd_32(&cs->cs_target_transfer_tag, 1); #if 0 CFISCSI_SESSION_DEBUG(cs, "expecting Data-Out with initiator " "task tag 0x%x, target transfer tag 0x%x", bhssc->bhssc_initiator_task_tag, target_transfer_tag); #endif cdw = uma_zalloc(cfiscsi_data_wait_zone, M_NOWAIT | M_ZERO); if (cdw == NULL) { CFISCSI_SESSION_WARN(cs, "failed to " "allocate memory; dropping connection"); ctl_set_busy(&io->scsiio); io->scsiio.be_move_done(io); cfiscsi_session_terminate(cs); return; } cdw->cdw_ctl_io = io; cdw->cdw_target_transfer_tag = target_transfer_tag; cdw->cdw_initiator_task_tag = bhssc->bhssc_initiator_task_tag; cdw->cdw_r2t_end = io->scsiio.kern_data_len; cdw->cdw_datasn = 0; /* Set initial data pointer for the CDW respecting ext_data_filled. */ if (io->scsiio.kern_sg_entries > 0) { ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; } else { ctl_sglist = &ctl_sg_entry; ctl_sglist->addr = io->scsiio.kern_data_ptr; ctl_sglist->len = io->scsiio.kern_data_len; } cdw->cdw_sg_index = 0; cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr; cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len; r2t_off = io->scsiio.ext_data_filled; while (r2t_off > 0) { if (r2t_off >= cdw->cdw_sg_len) { r2t_off -= cdw->cdw_sg_len; cdw->cdw_sg_index++; cdw->cdw_sg_addr = ctl_sglist[cdw->cdw_sg_index].addr; cdw->cdw_sg_len = ctl_sglist[cdw->cdw_sg_index].len; continue; } cdw->cdw_sg_addr += r2t_off; cdw->cdw_sg_len -= r2t_off; r2t_off = 0; } if (cs->cs_immediate_data && io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled < icl_pdu_data_segment_length(request)) { done = cfiscsi_handle_data_segment(request, cdw); if (done) { uma_zfree(cfiscsi_data_wait_zone, cdw); io->scsiio.be_move_done(io); return; } } r2t_off = io->scsiio.kern_rel_offset + io->scsiio.ext_data_filled; r2t_len = MIN(io->scsiio.kern_data_len - io->scsiio.ext_data_filled, cs->cs_max_burst_length); cdw->cdw_r2t_end = io->scsiio.ext_data_filled + r2t_len; CFISCSI_SESSION_LOCK(cs); TAILQ_INSERT_TAIL(&cs->cs_waiting_for_data_out, cdw, cdw_next); CFISCSI_SESSION_UNLOCK(cs); /* * XXX: We should limit the number of outstanding R2T PDUs * per task to MaxOutstandingR2T. */ response = cfiscsi_pdu_new_response(request, M_NOWAIT); if (response == NULL) { CFISCSI_SESSION_WARN(cs, "failed to " "allocate memory; dropping connection"); ctl_set_busy(&io->scsiio); io->scsiio.be_move_done(io); cfiscsi_session_terminate(cs); return; } bhsr2t = (struct iscsi_bhs_r2t *)response->ip_bhs; bhsr2t->bhsr2t_opcode = ISCSI_BHS_OPCODE_R2T; bhsr2t->bhsr2t_flags = 0x80; bhsr2t->bhsr2t_lun = bhssc->bhssc_lun; bhsr2t->bhsr2t_initiator_task_tag = bhssc->bhssc_initiator_task_tag; bhsr2t->bhsr2t_target_transfer_tag = target_transfer_tag; /* * XXX: Here we assume that cfiscsi_datamove() won't ever * be running concurrently on several CPUs for a given * command. */ bhsr2t->bhsr2t_r2tsn = htonl(PDU_R2TSN(request)); PDU_R2TSN(request)++; /* * This is the offset within the current SCSI command; * i.e. for the first call of datamove(), it will be 0, * and for subsequent ones it will be the sum of lengths * of previous ones. * * The ext_data_filled is to account for unsolicited * (immediate) data that might have already arrived. */ bhsr2t->bhsr2t_buffer_offset = htonl(r2t_off); /* * This is the total length (sum of S/G lengths) this call * to cfiscsi_datamove() is supposed to handle, limited by * MaxBurstLength. */ bhsr2t->bhsr2t_desired_data_transfer_length = htonl(r2t_len); cfiscsi_pdu_queue(response); } static void cfiscsi_datamove(union ctl_io *io) { if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) cfiscsi_datamove_in(io); else { /* We hadn't received anything during this datamove yet. */ io->scsiio.ext_data_filled = 0; cfiscsi_datamove_out(io); } } static void cfiscsi_scsi_command_done(union ctl_io *io) { struct icl_pdu *request, *response; struct iscsi_bhs_scsi_command *bhssc; struct iscsi_bhs_scsi_response *bhssr; #ifdef DIAGNOSTIC struct cfiscsi_data_wait *cdw; #endif struct cfiscsi_session *cs; uint16_t sense_length; request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs = PDU_SESSION(request); bhssc = (struct iscsi_bhs_scsi_command *)request->ip_bhs; KASSERT((bhssc->bhssc_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_SCSI_COMMAND, ("replying to wrong opcode 0x%x", bhssc->bhssc_opcode)); //CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x", // bhssc->bhssc_initiator_task_tag); #ifdef DIAGNOSTIC CFISCSI_SESSION_LOCK(cs); TAILQ_FOREACH(cdw, &cs->cs_waiting_for_data_out, cdw_next) KASSERT(bhssc->bhssc_initiator_task_tag != cdw->cdw_initiator_task_tag, ("dangling cdw")); CFISCSI_SESSION_UNLOCK(cs); #endif /* * Do not return status for aborted commands. * There are exceptions, but none supported by CTL yet. */ if (((io->io_hdr.flags & CTL_FLAG_ABORT) && (io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) || (io->io_hdr.flags & CTL_FLAG_STATUS_SENT)) { ctl_free_io(io); icl_pdu_free(request); return; } response = cfiscsi_pdu_new_response(request, M_WAITOK); bhssr = (struct iscsi_bhs_scsi_response *)response->ip_bhs; bhssr->bhssr_opcode = ISCSI_BHS_OPCODE_SCSI_RESPONSE; bhssr->bhssr_flags = 0x80; /* * XXX: We don't deal with bidirectional under/overflows; * does anything actually support those? */ if (PDU_TOTAL_TRANSFER_LEN(request) < ntohl(bhssc->bhssc_expected_data_transfer_length)) { bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_UNDERFLOW; bhssr->bhssr_residual_count = htonl(ntohl(bhssc->bhssc_expected_data_transfer_length) - PDU_TOTAL_TRANSFER_LEN(request)); //CFISCSI_SESSION_DEBUG(cs, "underflow; residual count %d", // ntohl(bhssr->bhssr_residual_count)); } else if (PDU_TOTAL_TRANSFER_LEN(request) > ntohl(bhssc->bhssc_expected_data_transfer_length)) { bhssr->bhssr_flags |= BHSSR_FLAGS_RESIDUAL_OVERFLOW; bhssr->bhssr_residual_count = htonl(PDU_TOTAL_TRANSFER_LEN(request) - ntohl(bhssc->bhssc_expected_data_transfer_length)); //CFISCSI_SESSION_DEBUG(cs, "overflow; residual count %d", // ntohl(bhssr->bhssr_residual_count)); } bhssr->bhssr_response = BHSSR_RESPONSE_COMMAND_COMPLETED; bhssr->bhssr_status = io->scsiio.scsi_status; bhssr->bhssr_initiator_task_tag = bhssc->bhssc_initiator_task_tag; bhssr->bhssr_expdatasn = htonl(PDU_EXPDATASN(request)); if (io->scsiio.sense_len > 0) { #if 0 CFISCSI_SESSION_DEBUG(cs, "returning %d bytes of sense data", io->scsiio.sense_len); #endif sense_length = htons(io->scsiio.sense_len); icl_pdu_append_data(response, &sense_length, sizeof(sense_length), M_WAITOK); icl_pdu_append_data(response, &io->scsiio.sense_data, io->scsiio.sense_len, M_WAITOK); } ctl_free_io(io); icl_pdu_free(request); cfiscsi_pdu_queue(response); } static void cfiscsi_task_management_done(union ctl_io *io) { struct icl_pdu *request, *response; struct iscsi_bhs_task_management_request *bhstmr; struct iscsi_bhs_task_management_response *bhstmr2; struct cfiscsi_data_wait *cdw, *tmpcdw; struct cfiscsi_session *cs; request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs = PDU_SESSION(request); bhstmr = (struct iscsi_bhs_task_management_request *)request->ip_bhs; KASSERT((bhstmr->bhstmr_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) == ISCSI_BHS_OPCODE_TASK_REQUEST, ("replying to wrong opcode 0x%x", bhstmr->bhstmr_opcode)); #if 0 CFISCSI_SESSION_DEBUG(cs, "initiator task tag 0x%x; referenced task tag 0x%x", bhstmr->bhstmr_initiator_task_tag, bhstmr->bhstmr_referenced_task_tag); #endif if ((bhstmr->bhstmr_function & ~0x80) == BHSTMR_FUNCTION_ABORT_TASK) { /* * Make sure we no longer wait for Data-Out for this command. */ CFISCSI_SESSION_LOCK(cs); TAILQ_FOREACH_SAFE(cdw, &cs->cs_waiting_for_data_out, cdw_next, tmpcdw) { if (bhstmr->bhstmr_referenced_task_tag != cdw->cdw_initiator_task_tag) continue; #if 0 CFISCSI_SESSION_DEBUG(cs, "removing csw for initiator task " "tag 0x%x", bhstmr->bhstmr_initiator_task_tag); #endif TAILQ_REMOVE(&cs->cs_waiting_for_data_out, cdw, cdw_next); cdw->cdw_ctl_io->scsiio.be_move_done(cdw->cdw_ctl_io); uma_zfree(cfiscsi_data_wait_zone, cdw); } CFISCSI_SESSION_UNLOCK(cs); } response = cfiscsi_pdu_new_response(request, M_WAITOK); bhstmr2 = (struct iscsi_bhs_task_management_response *) response->ip_bhs; bhstmr2->bhstmr_opcode = ISCSI_BHS_OPCODE_TASK_RESPONSE; bhstmr2->bhstmr_flags = 0x80; if (io->io_hdr.status == CTL_SUCCESS) { bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_COMPLETE; } else { /* * XXX: How to figure out what exactly went wrong? iSCSI spec * expects us to provide detailed error, e.g. "Task does * not exist" or "LUN does not exist". */ CFISCSI_SESSION_DEBUG(cs, "BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED"); bhstmr2->bhstmr_response = BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED; } bhstmr2->bhstmr_initiator_task_tag = bhstmr->bhstmr_initiator_task_tag; ctl_free_io(io); icl_pdu_free(request); cfiscsi_pdu_queue(response); } static void cfiscsi_done(union ctl_io *io) { struct icl_pdu *request; struct cfiscsi_session *cs; KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE), ("invalid CTL status %#x", io->io_hdr.status)); if (io->io_hdr.io_type == CTL_IO_TASK && io->taskio.task_action == CTL_TASK_I_T_NEXUS_RESET) { /* * Implicit task termination has just completed; nothing to do. */ cs = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs->cs_tasks_aborted = true; refcount_release(&cs->cs_outstanding_ctl_pdus); wakeup(__DEVOLATILE(void *, &cs->cs_outstanding_ctl_pdus)); ctl_free_io(io); return; } request = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; cs = PDU_SESSION(request); refcount_release(&cs->cs_outstanding_ctl_pdus); switch (request->ip_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) { case ISCSI_BHS_OPCODE_SCSI_COMMAND: cfiscsi_scsi_command_done(io); break; case ISCSI_BHS_OPCODE_TASK_REQUEST: cfiscsi_task_management_done(io); break; default: panic("cfiscsi_done called with wrong opcode 0x%x", request->ip_bhs->bhs_opcode); } } Index: projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.h =================================================================== --- projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.h (revision 278195) +++ projects/building-blocks/sys/cam/ctl/ctl_frontend_iscsi.h (revision 278196) @@ -1,125 +1,125 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala 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 CTL_FRONTEND_ISCSI_H #define CTL_FRONTEND_ISCSI_H #define CFISCSI_TARGET_STATE_INVALID 0 #define CFISCSI_TARGET_STATE_ACTIVE 1 #define CFISCSI_TARGET_STATE_DYING 2 struct cfiscsi_target { TAILQ_ENTRY(cfiscsi_target) ct_next; struct cfiscsi_softc *ct_softc; volatile u_int ct_refcount; char ct_name[CTL_ISCSI_NAME_LEN]; char ct_alias[CTL_ISCSI_ALIAS_LEN]; + uint16_t ct_tag; int ct_state; int ct_online; int ct_target_id; struct ctl_port ct_port; }; struct cfiscsi_data_wait { TAILQ_ENTRY(cfiscsi_data_wait) cdw_next; union ctl_io *cdw_ctl_io; uint32_t cdw_target_transfer_tag; uint32_t cdw_initiator_task_tag; int cdw_sg_index; char *cdw_sg_addr; size_t cdw_sg_len; uint32_t cdw_r2t_end; uint32_t cdw_datasn; }; #define CFISCSI_SESSION_STATE_INVALID 0 #define CFISCSI_SESSION_STATE_BHS 1 #define CFISCSI_SESSION_STATE_AHS 2 #define CFISCSI_SESSION_STATE_HEADER_DIGEST 3 #define CFISCSI_SESSION_STATE_DATA 4 #define CFISCSI_SESSION_STATE_DATA_DIGEST 5 struct cfiscsi_session { TAILQ_ENTRY(cfiscsi_session) cs_next; struct mtx cs_lock; struct icl_conn *cs_conn; uint32_t cs_cmdsn; uint32_t cs_statsn; uint32_t cs_target_transfer_tag; volatile u_int cs_outstanding_ctl_pdus; TAILQ_HEAD(, cfiscsi_data_wait) cs_waiting_for_data_out; struct cfiscsi_target *cs_target; struct callout cs_callout; int cs_timeout; - int cs_portal_group_tag; struct cv cs_maintenance_cv; bool cs_terminating; bool cs_tasks_aborted; size_t cs_max_data_segment_length; size_t cs_max_burst_length; bool cs_immediate_data; char cs_initiator_name[CTL_ISCSI_NAME_LEN]; char cs_initiator_addr[CTL_ISCSI_ADDR_LEN]; char cs_initiator_alias[CTL_ISCSI_ALIAS_LEN]; char cs_initiator_isid[6]; char cs_initiator_id[CTL_ISCSI_NAME_LEN + 5 + 6 + 1]; unsigned int cs_id; int cs_ctl_initid; #ifdef ICL_KERNEL_PROXY struct sockaddr *cs_initiator_sa; int cs_portal_id; bool cs_login_phase; bool cs_waiting_for_ctld; struct cv cs_login_cv; struct icl_pdu *cs_login_pdu; #endif }; #ifdef ICL_KERNEL_PROXY struct icl_listen; #endif struct cfiscsi_softc { struct mtx lock; char port_name[32]; int online; int last_target_id; unsigned int last_session_id; TAILQ_HEAD(, cfiscsi_target) targets; TAILQ_HEAD(, cfiscsi_session) sessions; struct cv sessions_cv; #ifdef ICL_KERNEL_PROXY struct icl_listen *listener; struct cv accept_cv; #endif }; #endif /* !CTL_FRONTEND_ISCSI_H */ Index: projects/building-blocks/sys/cam/scsi/scsi_cd.c =================================================================== --- projects/building-blocks/sys/cam/scsi/scsi_cd.c (revision 278195) +++ projects/building-blocks/sys/cam/scsi/scsi_cd.c (revision 278196) @@ -1,3705 +1,3718 @@ /*- * Copyright (c) 1997 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Kenneth D. Merry. * 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, immediately at the beginning of the file. * 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 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. */ /*- * Portions of this driver taken from the original FreeBSD cd driver. * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * * from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_cd.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 LEADOUT 0xaa /* leadout toc entry */ struct cd_params { u_int32_t blksize; u_long disksize; }; typedef enum { CD_Q_NONE = 0x00, CD_Q_NO_TOUCH = 0x01, CD_Q_BCD_TRACKS = 0x02, - CD_Q_10_BYTE_ONLY = 0x10 + CD_Q_10_BYTE_ONLY = 0x10, + CD_Q_RETRY_BUSY = 0x40 } cd_quirks; #define CD_Q_BIT_STRING \ "\020" \ "\001NO_TOUCH" \ "\002BCD_TRACKS" \ - "\00510_BYTE_ONLY" + "\00510_BYTE_ONLY" \ + "\007RETRY_BUSY" typedef enum { CD_FLAG_INVALID = 0x0001, CD_FLAG_NEW_DISC = 0x0002, CD_FLAG_DISC_LOCKED = 0x0004, CD_FLAG_DISC_REMOVABLE = 0x0008, CD_FLAG_SAW_MEDIA = 0x0010, CD_FLAG_ACTIVE = 0x0080, CD_FLAG_SCHED_ON_COMP = 0x0100, CD_FLAG_RETRY_UA = 0x0200, CD_FLAG_VALID_MEDIA = 0x0400, CD_FLAG_VALID_TOC = 0x0800, CD_FLAG_SCTX_INIT = 0x1000 } cd_flags; typedef enum { CD_CCB_PROBE = 0x01, CD_CCB_BUFFER_IO = 0x02, CD_CCB_TUR = 0x04, CD_CCB_TYPE_MASK = 0x0F, CD_CCB_RETRY_UA = 0x10 } cd_ccb_state; #define ccb_state ppriv_field0 #define ccb_bp ppriv_ptr1 struct cd_tocdata { struct ioc_toc_header header; struct cd_toc_entry entries[100]; }; struct cd_toc_single { struct ioc_toc_header header; struct cd_toc_entry entry; }; typedef enum { CD_STATE_PROBE, CD_STATE_NORMAL } cd_state; struct cd_softc { cam_pinfo pinfo; cd_state state; volatile cd_flags flags; struct bio_queue_head bio_queue; LIST_HEAD(, ccb_hdr) pending_ccbs; struct cd_params params; union ccb saved_ccb; cd_quirks quirks; struct cam_periph *periph; int minimum_command_size; int outstanding_cmds; int tur; struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; STAILQ_HEAD(, cd_mode_params) mode_queue; struct cd_tocdata toc; struct disk *disk; struct callout mediapoll_c; }; struct cd_page_sizes { int page; int page_size; }; static struct cd_page_sizes cd_page_size_table[] = { { AUDIO_PAGE, sizeof(struct cd_audio_page)} }; struct cd_quirk_entry { struct scsi_inquiry_pattern inq_pat; cd_quirks quirks; }; /* * NOTE ON 10_BYTE_ONLY quirks: Any 10_BYTE_ONLY quirks MUST be because * your device hangs when it gets a 10 byte command. Adding a quirk just * to get rid of the informative diagnostic message is not acceptable. All * 10_BYTE_ONLY quirks must be documented in full in a PR (which should be * referenced in a comment along with the quirk) , and must be approved by * ken@FreeBSD.org. Any quirks added that don't adhere to this policy may * be removed until the submitter can explain why they are needed. * 10_BYTE_ONLY quirks will be removed (as they will no longer be necessary) * when the CAM_NEW_TRAN_CODE work is done. */ static struct cd_quirk_entry cd_quirk_table[] = { { { T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"}, /* quirks */ CD_Q_BCD_TRACKS + }, + { + /* + * VMware returns BUSY status when storage has transient + * connectivity problems, so better wait. + */ + {T_CDROM, SIP_MEDIA_REMOVABLE, "NECVMWar", "VMware IDE CDR10", "*"}, + /*quirks*/ CD_Q_RETRY_BUSY } }; static disk_open_t cdopen; static disk_close_t cdclose; static disk_ioctl_t cdioctl; static disk_strategy_t cdstrategy; static periph_init_t cdinit; static periph_ctor_t cdregister; static periph_dtor_t cdcleanup; static periph_start_t cdstart; static periph_oninv_t cdoninvalidate; static void cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS); static int cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags), u_int32_t cam_flags, u_int32_t sense_flags); static void cddone(struct cam_periph *periph, union ccb *start_ccb); static union cd_pages *cdgetpage(struct cd_mode_params *mode_params); static int cdgetpagesize(int page_num); static void cdprevent(struct cam_periph *periph, int action); static int cdcheckmedia(struct cam_periph *periph); static int cdsize(struct cam_periph *periph, u_int32_t *size); static int cd6byteworkaround(union ccb *ccb); static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, u_int8_t *data, u_int32_t len, u_int32_t sense_flags); static int cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, struct cd_mode_params *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, u_int32_t format, int track, struct cd_sub_channel_info *data, u_int32_t len); static int cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf); static int cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, u_int32_t etrack, u_int32_t eindex); static int cdpause(struct cam_periph *periph, u_int32_t go); static int cdstopunit(struct cam_periph *periph, u_int32_t eject); static int cdstartunit(struct cam_periph *periph, int load); static int cdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed); static int cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo); static int cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo); static int cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct); static timeout_t cdmediapoll; static struct periph_driver cddriver = { cdinit, "cd", TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(cd, cddriver); #ifndef CD_DEFAULT_POLL_PERIOD #define CD_DEFAULT_POLL_PERIOD 3 #endif #ifndef CD_DEFAULT_RETRY #define CD_DEFAULT_RETRY 4 #endif #ifndef CD_DEFAULT_TIMEOUT #define CD_DEFAULT_TIMEOUT 30000 #endif static int cd_poll_period = CD_DEFAULT_POLL_PERIOD; static int cd_retry_count = CD_DEFAULT_RETRY; static int cd_timeout = CD_DEFAULT_TIMEOUT; static SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver"); SYSCTL_INT(_kern_cam_cd, OID_AUTO, poll_period, CTLFLAG_RWTUN, &cd_poll_period, 0, "Media polling period in seconds"); SYSCTL_INT(_kern_cam_cd, OID_AUTO, retry_count, CTLFLAG_RWTUN, &cd_retry_count, 0, "Normal I/O retry count"); SYSCTL_INT(_kern_cam_cd, OID_AUTO, timeout, CTLFLAG_RWTUN, &cd_timeout, 0, "Timeout, in us, for read operations"); static MALLOC_DEFINE(M_SCSICD, "scsi_cd", "scsi_cd buffers"); static void cdinit(void) { cam_status status; /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, cdasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("cd: Failed to attach master async callback " "due to status 0x%x!\n", status); } } /* * Callback from GEOM, called when it has finished cleaning up its * resources. */ static void cddiskgonecb(struct disk *dp) { struct cam_periph *periph; periph = (struct cam_periph *)dp->d_drv1; cam_periph_release(periph); } static void cdoninvalidate(struct cam_periph *periph) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_register_async(0, cdasync, periph, periph->path); softc->flags |= CD_FLAG_INVALID; /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ bioq_flush(&softc->bio_queue, NULL, ENXIO); disk_gone(softc->disk); } static void cdcleanup(struct cam_periph *periph) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; cam_periph_unlock(periph); if ((softc->flags & CD_FLAG_SCTX_INIT) != 0 && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { xpt_print(periph->path, "can't remove sysctl context\n"); } callout_drain(&softc->mediapoll_c); disk_destroy(softc->disk); free(softc, M_DEVBUF); cam_periph_lock(periph); } static void cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; struct cd_softc *softc; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; if (cgd->protocol != PROTO_SCSI) break; if (SID_TYPE(&cgd->inq_data) != T_CDROM && SID_TYPE(&cgd->inq_data) != T_WORM) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ status = cam_periph_alloc(cdregister, cdoninvalidate, cdcleanup, cdstart, "cd", CAM_PERIPH_BIO, path, cdasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("cdasync: Unable to attach new device " "due to status 0x%x\n", status); break; } case AC_UNIT_ATTENTION: { union ccb *ccb; int error_code, sense_key, asc, ascq; softc = (struct cd_softc *)periph->softc; ccb = (union ccb *)arg; /* * Handle all media change UNIT ATTENTIONs except * our own, as they will be handled by cderror(). */ if (xpt_path_periph(ccb->ccb_h.path) != periph && scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq)) { if (asc == 0x28 && ascq == 0x00) disk_media_changed(softc->disk, M_NOWAIT); } cam_periph_async(periph, code, path, arg); break; } case AC_SCSI_AEN: softc = (struct cd_softc *)periph->softc; if (softc->state == CD_STATE_NORMAL && !softc->tur) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { softc->tur = 1; xpt_schedule(periph, CAM_PRIORITY_NORMAL); } } /* FALLTHROUGH */ case AC_SENT_BDR: case AC_BUS_RESET: { struct ccb_hdr *ccbh; softc = (struct cd_softc *)periph->softc; /* * Don't fail on the expected unit attention * that will occur. */ softc->flags |= CD_FLAG_RETRY_UA; LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) ccbh->ccb_state |= CD_CCB_RETRY_UA; /* FALLTHROUGH */ } default: cam_periph_async(periph, code, path, arg); break; } } static void cdsysctlinit(void *context, int pending) { struct cam_periph *periph; struct cd_softc *softc; char tmpstr[80], tmpstr2[80]; periph = (struct cam_periph *)context; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return; softc = (struct cd_softc *)periph->softc; snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); sysctl_ctx_init(&softc->sysctl_ctx); softc->flags |= CD_FLAG_SCTX_INIT; softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO, tmpstr2, CTLFLAG_RD, 0, tmpstr); if (softc->sysctl_tree == NULL) { printf("cdsysctlinit: unable to allocate sysctl tree\n"); cam_periph_release(periph); return; } /* * Now register the sysctl handler, so the user can the value on * the fly. */ SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_command_size, 0, cdcmdsizesysctl, "I", "Minimum CDB size"); cam_periph_release(periph); } /* * We have a handler function for this so we can check the values when the * user sets them, instead of every time we look at them. */ static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS) { int error, value; value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if ((error != 0) || (req->newptr == NULL)) return (error); /* * The only real values we can have here are 6 or 10. I don't * really forsee having 12 be an option at any time in the future. * So if the user sets something less than or equal to 6, we'll set * it to 6. If he sets something greater than 6, we'll set it to 10. * * I suppose we could just return an error here for the wrong values, * but I don't think it's necessary to do so, as long as we can * determine the user's intent without too much trouble. */ if (value < 6) value = 6; else if (value > 6) value = 10; *(int *)arg1 = value; return (0); } static cam_status cdregister(struct cam_periph *periph, void *arg) { struct cd_softc *softc; struct ccb_pathinq cpi; struct ccb_getdev *cgd; char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) { printf("cdregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF, M_NOWAIT | M_ZERO); if (softc == NULL) { printf("cdregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } LIST_INIT(&softc->pending_ccbs); STAILQ_INIT(&softc->mode_queue); softc->state = CD_STATE_PROBE; bioq_init(&softc->bio_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= CD_FLAG_DISC_REMOVABLE; periph->softc = softc; softc->periph = periph; /* * See if this device has any quirks. */ match = cam_quirkmatch((caddr_t)&cgd->inq_data, (caddr_t)cd_quirk_table, sizeof(cd_quirk_table)/sizeof(*cd_quirk_table), sizeof(*cd_quirk_table), scsi_inquiry_match); if (match != NULL) softc->quirks = ((struct cd_quirk_entry *)match)->quirks; else softc->quirks = CD_Q_NONE; /* Check if the SIM does not want 6 byte commands */ bzero(&cpi, sizeof(cpi)); xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= CD_Q_10_BYTE_ONLY; TASK_INIT(&softc->sysctl_task, 0, cdsysctlinit, periph); /* The default is 6 byte commands, unless quirked otherwise */ if (softc->quirks & CD_Q_10_BYTE_ONLY) softc->minimum_command_size = 10; else softc->minimum_command_size = 6; /* * Refcount and block open attempts until we are setup * Can't block */ (void)cam_periph_hold(periph, PRIBIO); cam_periph_unlock(periph); /* * Load the user's default, if any. */ snprintf(tmpstr, sizeof(tmpstr), "kern.cam.cd.%d.minimum_cmd_size", periph->unit_number); TUNABLE_INT_FETCH(tmpstr, &softc->minimum_command_size); /* 6 and 10 are the only permissible values here. */ if (softc->minimum_command_size < 6) softc->minimum_command_size = 6; else if (softc->minimum_command_size > 6) softc->minimum_command_size = 10; /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register * the structure and indicate that we don't have the blocksize * yet. Unlike other SCSI peripheral drivers, we explicitly set * the device type here to be CDROM, rather than just ORing in * the device type. This is because this driver can attach to either * CDROM or WORM devices, and we want this peripheral driver to * show up in the devstat list as a CD peripheral driver, not a * WORM peripheral driver. WORM drives will also have the WORM * driver attached to them. */ softc->disk = disk_alloc(); softc->disk->d_devstat = devstat_new_entry("cd", periph->unit_number, 0, DEVSTAT_BS_UNAVAILABLE, DEVSTAT_TYPE_CDROM | XPORT_DEVSTAT_TYPE(cpi.transport), DEVSTAT_PRIORITY_CD); softc->disk->d_open = cdopen; softc->disk->d_close = cdclose; softc->disk->d_strategy = cdstrategy; softc->disk->d_gone = cddiskgonecb; softc->disk->d_ioctl = cdioctl; softc->disk->d_name = "cd"; cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); cam_strvis(&softc->disk->d_descr[strlen(softc->disk->d_descr)], cgd->inq_data.product, sizeof(cgd->inq_data.product), sizeof(softc->disk->d_descr) - strlen(softc->disk->d_descr)); softc->disk->d_unit = periph->unit_number; softc->disk->d_drv1 = periph; if (cpi.maxio == 0) softc->disk->d_maxsize = DFLTPHYS; /* traditional default */ else if (cpi.maxio > MAXPHYS) softc->disk->d_maxsize = MAXPHYS; /* for safety */ else softc->disk->d_maxsize = cpi.maxio; softc->disk->d_flags = 0; softc->disk->d_hba_vendor = cpi.hba_vendor; softc->disk->d_hba_device = cpi.hba_device; softc->disk->d_hba_subvendor = cpi.hba_subvendor; softc->disk->d_hba_subdevice = cpi.hba_subdevice; /* * Acquire a reference to the periph before we register with GEOM. * We'll release this reference once GEOM calls us back (via * dadiskgonecb()) telling us that our provider has been freed. */ if (cam_periph_acquire(periph) != CAM_REQ_CMP) { xpt_print(periph->path, "%s: lost periph during " "registration!\n", __func__); cam_periph_lock(periph); return (CAM_REQ_CMP_ERR); } disk_create(softc->disk, DISK_VERSION); cam_periph_lock(periph); /* * Add an async callback so that we get * notified if this device goes away. */ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | AC_SCSI_AEN | AC_UNIT_ATTENTION, cdasync, periph, periph->path); /* * Schedule a periodic media polling events. */ callout_init_mtx(&softc->mediapoll_c, cam_periph_mtx(periph), 0); if ((softc->flags & CD_FLAG_DISC_REMOVABLE) && (cgd->inq_flags & SID_AEN) == 0 && cd_poll_period != 0) callout_reset(&softc->mediapoll_c, cd_poll_period * hz, cdmediapoll, periph); xpt_schedule(periph, CAM_PRIORITY_DEV); return(CAM_REQ_CMP); } static int cdopen(struct disk *dp) { struct cam_periph *periph; struct cd_softc *softc; int error; periph = (struct cam_periph *)dp->d_drv1; softc = (struct cd_softc *)periph->softc; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return(ENXIO); cam_periph_lock(periph); if (softc->flags & CD_FLAG_INVALID) { cam_periph_release_locked(periph); cam_periph_unlock(periph); return(ENXIO); } if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { cam_periph_release_locked(periph); cam_periph_unlock(periph); return (error); } CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, ("cdopen\n")); /* * Check for media, and set the appropriate flags. We don't bail * if we don't have media, but then we don't allow anything but the * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media. */ cdcheckmedia(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n")); cam_periph_unhold(periph); cam_periph_unlock(periph); return (0); } static int cdclose(struct disk *dp) { struct cam_periph *periph; struct cd_softc *softc; periph = (struct cam_periph *)dp->d_drv1; softc = (struct cd_softc *)periph->softc; cam_periph_lock(periph); if (cam_periph_hold(periph, PRIBIO) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (0); } CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, ("cdclose\n")); if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0) cdprevent(periph, PR_ALLOW); /* * Since we're closing this CD, mark the blocksize as unavailable. * It will be marked as available when the CD is opened again. */ softc->disk->d_devstat->flags |= DEVSTAT_BS_UNAVAILABLE; /* * We'll check the media and toc again at the next open(). */ softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); cam_periph_unhold(periph); cam_periph_release_locked(periph); cam_periph_unlock(periph); return (0); } static int cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags), u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags, softc->disk->d_devstat); return(error); } /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include * only one physical transfer. */ static void cdstrategy(struct bio *bp) { struct cam_periph *periph; struct cd_softc *softc; periph = (struct cam_periph *)bp->bio_disk->d_drv1; cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("cdstrategy(%p)\n", bp)); softc = (struct cd_softc *)periph->softc; /* * If the device has been made invalid, error out */ if ((softc->flags & CD_FLAG_INVALID)) { cam_periph_unlock(periph); biofinish(bp, NULL, ENXIO); return; } /* * If we don't have valid media, look for it before trying to * schedule the I/O. */ if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) { int error; error = cdcheckmedia(periph); if (error != 0) { cam_periph_unlock(periph); biofinish(bp, NULL, error); return; } } /* * Place it in the queue of disk activities for this disk */ bioq_disksort(&softc->bio_queue, bp); xpt_schedule(periph, CAM_PRIORITY_NORMAL); cam_periph_unlock(periph); return; } static void cdstart(struct cam_periph *periph, union ccb *start_ccb) { struct cd_softc *softc; struct bio *bp; struct ccb_scsiio *csio; struct scsi_read_capacity_data *rcap; softc = (struct cd_softc *)periph->softc; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstart\n")); switch (softc->state) { case CD_STATE_NORMAL: { bp = bioq_first(&softc->bio_queue); if (bp == NULL) { if (softc->tur) { softc->tur = 0; csio = &start_ccb->csio; scsi_test_unit_ready(csio, /*retries*/ cd_retry_count, cddone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, cd_timeout); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = CD_CCB_TUR; xpt_action(start_ccb); } else xpt_release_ccb(start_ccb); } else { if (softc->tur) { softc->tur = 0; cam_periph_release_locked(periph); } bioq_remove(&softc->bio_queue, bp); scsi_read_write(&start_ccb->csio, /*retries*/ cd_retry_count, /* cbfcnp */ cddone, MSG_SIMPLE_Q_TAG, /* read */bp->bio_cmd == BIO_READ ? SCSI_RW_READ : SCSI_RW_WRITE, /* byte2 */ 0, /* minimum_cmd_size */ 10, /* lba */ bp->bio_offset / softc->params.blksize, bp->bio_bcount / softc->params.blksize, /* data_ptr */ bp->bio_data, /* dxfer_len */ bp->bio_bcount, /* sense_len */ cd_retry_count ? SSD_FULL_SIZE : SF_NO_PRINT, /* timeout */ cd_timeout); /* Use READ CD command for audio tracks. */ if (softc->params.blksize == 2352) { start_ccb->csio.cdb_io.cdb_bytes[0] = READ_CD; start_ccb->csio.cdb_io.cdb_bytes[9] = 0xf8; start_ccb->csio.cdb_io.cdb_bytes[10] = 0; start_ccb->csio.cdb_io.cdb_bytes[11] = 0; start_ccb->csio.cdb_len = 12; } start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO; LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, periph_links.le); softc->outstanding_cmds++; /* We expect a unit attention from this device */ if ((softc->flags & CD_FLAG_RETRY_UA) != 0) { start_ccb->ccb_h.ccb_state |= CD_CCB_RETRY_UA; softc->flags &= ~CD_FLAG_RETRY_UA; } start_ccb->ccb_h.ccb_bp = bp; bp = bioq_first(&softc->bio_queue); xpt_action(start_ccb); } if (bp != NULL || softc->tur) { /* Have more work to do, so ensure we stay scheduled */ xpt_schedule(periph, CAM_PRIORITY_NORMAL); } break; } case CD_STATE_PROBE: { rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap), M_SCSICD, M_NOWAIT | M_ZERO); if (rcap == NULL) { xpt_print(periph->path, "cdstart: Couldn't malloc read_capacity data\n"); /* cd_free_periph??? */ break; } csio = &start_ccb->csio; scsi_read_capacity(csio, /*retries*/ cd_retry_count, cddone, MSG_SIMPLE_Q_TAG, rcap, SSD_FULL_SIZE, /*timeout*/20000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = CD_CCB_PROBE; xpt_action(start_ccb); break; } } } static void cddone(struct cam_periph *periph, union ccb *done_ccb) { struct cd_softc *softc; struct ccb_scsiio *csio; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cddone\n")); softc = (struct cd_softc *)periph->softc; csio = &done_ccb->csio; switch (csio->ccb_h.ccb_state & CD_CCB_TYPE_MASK) { case CD_CCB_BUFFER_IO: { struct bio *bp; int error; bp = (struct bio *)done_ccb->ccb_h.ccb_bp; error = 0; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { int sf; if ((done_ccb->ccb_h.ccb_state & CD_CCB_RETRY_UA) != 0) sf = SF_RETRY_UA; else sf = 0; error = cderror(done_ccb, CAM_RETRY_SELTO, sf); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } } if (error != 0) { xpt_print(periph->path, "cddone: got error %#x back\n", error); bioq_flush(&softc->bio_queue, NULL, EIO); bp->bio_resid = bp->bio_bcount; bp->bio_error = error; bp->bio_flags |= BIO_ERROR; if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } else { bp->bio_resid = csio->resid; bp->bio_error = 0; if (bp->bio_resid != 0) { /* * Short transfer ??? * XXX: not sure this is correct for partial * transfers at EOM */ bp->bio_flags |= BIO_ERROR; } } LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); softc->outstanding_cmds--; biofinish(bp, NULL, 0); break; } case CD_CCB_PROBE: { struct scsi_read_capacity_data *rdcap; char announce_buf[120]; /* * Currently (9/30/97) the * longest possible announce * buffer is 108 bytes, for the * first error case below. * That is 39 bytes for the * basic string, 16 bytes for the * biggest sense key (hardware * error), 52 bytes for the * text of the largest sense * qualifier valid for a CDROM, * (0x72, 0x03 or 0x04, * 0x03), and one byte for the * null terminating character. * To allow for longer strings, * the announce buffer is 120 * bytes. */ struct cd_params *cdp; int error; cdp = &softc->params; rdcap = (struct scsi_read_capacity_data *)csio->data_ptr; cdp->disksize = scsi_4btoul (rdcap->addr) + 1; cdp->blksize = scsi_4btoul (rdcap->length); /* * Retry any UNIT ATTENTION type errors. They * are expected at boot. */ if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP || (error = cderror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_PRINT)) == 0) { snprintf(announce_buf, sizeof(announce_buf), "cd present [%lu x %lu byte records]", cdp->disksize, (u_long)cdp->blksize); } else { if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } else { int asc, ascq; int sense_key, error_code; int have_sense; cam_status status; struct ccb_getdev cgd; /* Don't wedge this device's queue */ if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); status = done_ccb->ccb_h.status; xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if (scsi_extract_sense_ccb(done_ccb, &error_code, &sense_key, &asc, &ascq)) have_sense = TRUE; else have_sense = FALSE; /* * Attach to anything that claims to be a * CDROM or WORM device, as long as it * doesn't return a "Logical unit not * supported" (0x25) error. */ if ((have_sense) && (asc != 0x25) && (error_code == SSD_CURRENT_ERROR)) { const char *sense_key_desc; const char *asc_desc; scsi_sense_desc(sense_key, asc, ascq, &cgd.inq_data, &sense_key_desc, &asc_desc); snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: %s, %s", sense_key_desc, asc_desc); } else if ((have_sense == 0) && ((status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (csio->scsi_status == SCSI_STATUS_BUSY)) { snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: SCSI Status: %s", scsi_status_string(csio)); } else if (SID_TYPE(&cgd.inq_data) == T_CDROM) { /* * We only print out an error for * CDROM type devices. For WORM * devices, we don't print out an * error since a few WORM devices * don't support CDROM commands. * If we have sense information, go * ahead and print it out. * Otherwise, just say that we * couldn't attach. */ /* * Just print out the error, not * the full probe message, when we * don't attach. */ if (have_sense) scsi_sense_print( &done_ccb->csio); else { xpt_print(periph->path, "got CAM status %#x\n", done_ccb->ccb_h.status); } xpt_print(periph->path, "fatal error, " "failed to attach to device\n"); /* * Invalidate this peripheral. */ cam_periph_invalidate(periph); announce_buf[0] = '\0'; } else { /* * Invalidate this peripheral. */ cam_periph_invalidate(periph); announce_buf[0] = '\0'; } } } free(rdcap, M_SCSICD); if (announce_buf[0] != '\0') { xpt_announce_periph(periph, announce_buf); xpt_announce_quirks(periph, softc->quirks, CD_Q_BIT_STRING); /* * Create our sysctl variables, now that we know * we have successfully attached. */ taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); } softc->state = CD_STATE_NORMAL; /* * Since our peripheral may be invalidated by an error * above or an external event, we must release our CCB * before releasing the probe lock on the peripheral. * The peripheral will only go away once the last lock * is removed, and we need it around for the CCB release * operation. */ xpt_release_ccb(done_ccb); cam_periph_unhold(periph); return; } case CD_CCB_TUR: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (cderror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) == ERESTART) return; if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } xpt_release_ccb(done_ccb); cam_periph_release_locked(periph); return; } default: break; } xpt_release_ccb(done_ccb); } static union cd_pages * cdgetpage(struct cd_mode_params *mode_params) { union cd_pages *page; if (mode_params->cdb_size == 10) page = (union cd_pages *)find_mode_page_10( (struct scsi_mode_header_10 *)mode_params->mode_buf); else page = (union cd_pages *)find_mode_page_6( (struct scsi_mode_header_6 *)mode_params->mode_buf); return (page); } static int cdgetpagesize(int page_num) { int i; for (i = 0; i < (sizeof(cd_page_size_table)/ sizeof(cd_page_size_table[0])); i++) { if (cd_page_size_table[i].page == page_num) return (cd_page_size_table[i].page_size); } return (-1); } static int cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) { struct cam_periph *periph; struct cd_softc *softc; int nocopyout, error = 0; periph = (struct cam_periph *)dp->d_drv1; cam_periph_lock(periph); softc = (struct cd_softc *)periph->softc; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("cdioctl(%#lx)\n", cmd)); if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } /* * If we don't have media loaded, check for it. If still don't * have media loaded, we can only do a load or eject. * * We only care whether media is loaded if this is a cd-specific ioctl * (thus the IOCGROUP check below). Note that this will break if * anyone adds any ioctls into the switch statement below that don't * have their ioctl group set to 'c'. */ if (((softc->flags & CD_FLAG_VALID_MEDIA) == 0) && ((cmd != CDIOCCLOSE) && (cmd != CDIOCEJECT)) && (IOCGROUP(cmd) == 'c')) { error = cdcheckmedia(periph); if (error != 0) { cam_periph_unhold(periph); cam_periph_unlock(periph); return (error); } } /* * Drop the lock here so later mallocs can use WAITOK. The periph * is essentially locked still with the cam_periph_hold call above. */ cam_periph_unlock(periph); nocopyout = 0; switch (cmd) { case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } /* * This was originally implemented with the PLAY * AUDIO TRACK INDEX command, but that command was * deprecated after SCSI-2. Most (all?) SCSI CDROM * drives support it but ATAPI and ATAPI-derivative * drives don't seem to support it. So we keep a * cache of the table of contents and translate * track numbers to MSF format. */ if (softc->flags & CD_FLAG_VALID_TOC) { union msf_lba *sentry, *eentry; int st, et; if (args->end_track < softc->toc.header.ending_track + 1) args->end_track++; if (args->end_track > softc->toc.header.ending_track + 1) args->end_track = softc->toc.header.ending_track + 1; st = args->start_track - softc->toc.header.starting_track; et = args->end_track - softc->toc.header.starting_track; if ((st < 0) || (et < 0) || (st > (softc->toc.header.ending_track - softc->toc.header.starting_track))) { error = EINVAL; cam_periph_unlock(periph); break; } sentry = &softc->toc.entries[st].addr; eentry = &softc->toc.entries[et].addr; error = cdplaymsf(periph, sentry->msf.minute, sentry->msf.second, sentry->msf.frame, eentry->msf.minute, eentry->msf.second, eentry->msf.frame); } else { /* * If we don't have a valid TOC, try the * play track index command. It is part of * the SCSI-2 spec, but was removed in the * MMC specs. ATAPI and ATAPI-derived * drives don't support it. */ if (softc->quirks & CD_Q_BCD_TRACKS) { args->start_track = bin2bcd(args->start_track); args->end_track = bin2bcd(args->end_track); } error = cdplaytracks(periph, args->start_track, args->start_index, args->end_track, args->end_index); } cam_periph_unlock(periph); } break; case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } error = cdplaymsf(periph, args->start_m, args->start_s, args->start_f, args->end_m, args->end_s, args->end_f); cam_periph_unlock(periph); } break; case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } error = cdplay(periph, args->blk, args->len); cam_periph_unlock(periph); } break; case CDIOCREADSUBCHANNEL_SYSSPACE: nocopyout = 1; /* Fallthrough */ case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *) addr; struct cd_sub_channel_info *data; u_int32_t len = args->data_len; data = malloc(sizeof(struct cd_sub_channel_info), M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCREADSUBCHANNEL\n")); if ((len > sizeof(struct cd_sub_channel_info)) || (len < sizeof(struct cd_sub_channel_header))) { printf( "scsi_cd: cdioctl: " "cdioreadsubchannel: error, len=%d\n", len); error = EINVAL; free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) args->track = bin2bcd(args->track); error = cdreadsubchannel(periph, args->address_format, args->data_format, args->track, data, len); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) data->what.track_info.track_number = bcd2bin(data->what.track_info.track_number); len = min(len, ((data->header.data_len[0] << 8) + data->header.data_len[1] + sizeof(struct cd_sub_channel_header))); cam_periph_unlock(periph); if (nocopyout == 0) { if (copyout(data, args->data, len) != 0) { error = EFAULT; } } else { bcopy(data, args->data, len); } free(data, M_SCSICD); } break; case CDIOREADTOCHEADER: { struct ioc_toc_header *th; th = malloc(sizeof(struct ioc_toc_header), M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCHEADER\n")); error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/SF_NO_PRINT); if (error) { free(th, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } th->len = ntohs(th->len); bcopy(th, addr, sizeof(*th)); free(th, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOREADTOCENTRYS: { struct cd_tocdata *data; struct cd_toc_single *lead; struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *) addr; struct ioc_toc_header *th; u_int32_t len, readlen, idx, num; u_int32_t starting_track = te->starting_track; data = malloc(sizeof(*data), M_SCSICD, M_WAITOK | M_ZERO); lead = malloc(sizeof(*lead), M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCENTRYS\n")); if (te->data_len < sizeof(struct cd_toc_entry) || (te->data_len % sizeof(struct cd_toc_entry)) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT)) { error = EINVAL; printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } th = &data->header; error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } if (starting_track == 0) starting_track = th->starting_track; else if (starting_track == LEADOUT) starting_track = th->ending_track + 1; else if (starting_track < th->starting_track || starting_track > th->ending_track + 1) { printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); error = EINVAL; break; } /* calculate reading length without leadout entry */ readlen = (th->ending_track - starting_track + 1) * sizeof(struct cd_toc_entry); /* and with leadout entry */ len = readlen + sizeof(struct cd_toc_entry); if (te->data_len < len) { len = te->data_len; if (readlen > len) readlen = len; } if (len > sizeof(data->entries)) { printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); error = EINVAL; free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } num = len / sizeof(struct cd_toc_entry); if (readlen > 0) { error = cdreadtoc(periph, te->address_format, starting_track, (u_int8_t *)data, readlen + sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } } /* make leadout entry if needed */ idx = starting_track + num - 1; if (softc->quirks & CD_Q_BCD_TRACKS) th->ending_track = bcd2bin(th->ending_track); if (idx == th->ending_track + 1) { error = cdreadtoc(periph, te->address_format, LEADOUT, (u_int8_t *)lead, sizeof(*lead), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } data->entries[idx - starting_track] = lead->entry; } if (softc->quirks & CD_Q_BCD_TRACKS) { for (idx = 0; idx < num - 1; idx++) { data->entries[idx].track = bcd2bin(data->entries[idx].track); } } cam_periph_unlock(periph); error = copyout(data->entries, te->data, len); free(data, M_SCSICD); free(lead, M_SCSICD); } break; case CDIOREADTOCENTRY: { struct cd_toc_single *data; struct ioc_read_toc_single_entry *te = (struct ioc_read_toc_single_entry *) addr; struct ioc_toc_header *th; u_int32_t track; data = malloc(sizeof(*data), M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCENTRY\n")); if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) { printf("error in readtocentry, " " returning EINVAL\n"); free(data, M_SCSICD); error = EINVAL; cam_periph_unlock(periph); break; } th = &data->header; error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } track = te->track; if (track == 0) track = th->starting_track; else if (track == LEADOUT) /* OK */; else if (track < th->starting_track || track > th->ending_track + 1) { printf("error in readtocentry, " " returning EINVAL\n"); free(data, M_SCSICD); error = EINVAL; cam_periph_unlock(periph); break; } error = cdreadtoc(periph, te->address_format, track, (u_int8_t *)data, sizeof(*data), /*sense_flags*/0); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) data->entry.track = bcd2bin(data->entry.track); bcopy(&data->entry, &te->entry, sizeof(struct cd_toc_entry)); free(data, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = arg->patch[0]; page->audio.port[RIGHT_PORT].channels = arg->patch[1]; page->audio.port[2].channels = arg->patch[2]; page->audio.port[3].channels = arg->patch[3]; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); arg->vol[LEFT_PORT] = page->audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = page->audio.port[RIGHT_PORT].volume; arg->vol[2] = page->audio.port[2].volume; arg->vol[3] = page->audio.port[3].volume; free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = CHANNEL_0; page->audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; page->audio.port[RIGHT_PORT].channels = CHANNEL_1; page->audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; page->audio.port[2].volume = arg->vol[2]; page->audio.port[3].volume = arg->vol[3]; error = cdsetmode(periph, ¶ms); cam_periph_unlock(periph); free(params.mode_buf, M_SCSICD); } break; case CDIOCSETMONO: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); cam_periph_unlock(periph); free(params.mode_buf, M_SCSICD); } break; case CDIOCSETSTEREO: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETMUTE: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = 0; page->audio.port[RIGHT_PORT].channels = 0; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETLEFT: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETRIGHT: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCRESUME: cam_periph_lock(periph); error = cdpause(periph, 1); cam_periph_unlock(periph); break; case CDIOCPAUSE: cam_periph_lock(periph); error = cdpause(periph, 0); cam_periph_unlock(periph); break; case CDIOCSTART: cam_periph_lock(periph); error = cdstartunit(periph, 0); cam_periph_unlock(periph); break; case CDIOCCLOSE: cam_periph_lock(periph); error = cdstartunit(periph, 1); cam_periph_unlock(periph); break; case CDIOCSTOP: cam_periph_lock(periph); error = cdstopunit(periph, 0); cam_periph_unlock(periph); break; case CDIOCEJECT: cam_periph_lock(periph); error = cdstopunit(periph, 1); cam_periph_unlock(periph); break; case CDIOCALLOW: cam_periph_lock(periph); cdprevent(periph, PR_ALLOW); cam_periph_unlock(periph); break; case CDIOCPREVENT: cam_periph_lock(periph); cdprevent(periph, PR_PREVENT); cam_periph_unlock(periph); break; case CDIOCSETDEBUG: /* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */ error = ENOTTY; break; case CDIOCCLRDEBUG: /* sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); */ error = ENOTTY; break; case CDIOCRESET: /* return (cd_reset(periph)); */ error = ENOTTY; break; case CDRIOCREADSPEED: cam_periph_lock(periph); error = cdsetspeed(periph, *(u_int32_t *)addr, CDR_MAX_SPEED); cam_periph_unlock(periph); break; case CDRIOCWRITESPEED: cam_periph_lock(periph); error = cdsetspeed(periph, CDR_MAX_SPEED, *(u_int32_t *)addr); cam_periph_unlock(periph); break; case CDRIOCGETBLOCKSIZE: *(int *)addr = softc->params.blksize; break; case CDRIOCSETBLOCKSIZE: if (*(int *)addr <= 0) { error = EINVAL; break; } softc->disk->d_sectorsize = softc->params.blksize = *(int *)addr; break; case DVDIOCSENDKEY: case DVDIOCREPORTKEY: { struct dvd_authinfo *authinfo; authinfo = (struct dvd_authinfo *)addr; if (cmd == DVDIOCREPORTKEY) error = cdreportkey(periph, authinfo); else error = cdsendkey(periph, authinfo); break; } case DVDIOCREADSTRUCTURE: { struct dvd_struct *dvdstruct; dvdstruct = (struct dvd_struct *)addr; error = cdreaddvdstructure(periph, dvdstruct); break; } default: cam_periph_lock(periph); error = cam_periph_ioctl(periph, cmd, addr, cderror); cam_periph_unlock(periph); break; } cam_periph_lock(periph); cam_periph_unhold(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n")); if (error && bootverbose) { printf("scsi_cd.c::ioctl cmd=%08lx error=%d\n", cmd, error); } cam_periph_unlock(periph); return (error); } static void cdprevent(struct cam_periph *periph, int action) { union ccb *ccb; struct cd_softc *softc; int error; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdprevent\n")); softc = (struct cd_softc *)periph->softc; if (((action == PR_ALLOW) && (softc->flags & CD_FLAG_DISC_LOCKED) == 0) || ((action == PR_PREVENT) && (softc->flags & CD_FLAG_DISC_LOCKED) != 0)) { return; } ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_prevent(&ccb->csio, /*retries*/ cd_retry_count, cddone, MSG_SIMPLE_Q_TAG, action, SSD_FULL_SIZE, /* timeout */60000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); xpt_release_ccb(ccb); if (error == 0) { if (action == PR_ALLOW) softc->flags &= ~CD_FLAG_DISC_LOCKED; else softc->flags |= CD_FLAG_DISC_LOCKED; } } /* * XXX: the disk media and sector size is only really able to change * XXX: while the device is closed. */ static int cdcheckmedia(struct cam_periph *periph) { struct cd_softc *softc; struct ioc_toc_header *toch; struct cd_toc_single leadout; u_int32_t size, toclen; int error, num_entries, cdindex; softc = (struct cd_softc *)periph->softc; cdprevent(periph, PR_PREVENT); softc->disk->d_sectorsize = 2048; softc->disk->d_mediasize = 0; /* * Get the disc size and block size. If we can't get it, we don't * have media, most likely. */ if ((error = cdsize(periph, &size)) != 0) { softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); cdprevent(periph, PR_ALLOW); return (error); } else { softc->flags |= CD_FLAG_SAW_MEDIA | CD_FLAG_VALID_MEDIA; softc->disk->d_sectorsize = softc->params.blksize; softc->disk->d_mediasize = (off_t)softc->params.blksize * softc->params.disksize; } /* * Now we check the table of contents. This (currently) is only * used for the CDIOCPLAYTRACKS ioctl. It may be used later to do * things like present a separate entry in /dev for each track, * like that acd(4) driver does. */ bzero(&softc->toc, sizeof(softc->toc)); toch = &softc->toc.header; /* * We will get errors here for media that doesn't have a table of * contents. According to the MMC-3 spec: "When a Read TOC/PMA/ATIP * command is presented for a DDCD/CD-R/RW media, where the first TOC * has not been recorded (no complete session) and the Format codes * 0000b, 0001b, or 0010b are specified, this command shall be rejected * with an INVALID FIELD IN CDB. Devices that are not capable of * reading an incomplete session on DDC/CD-R/RW media shall report * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT." * * So this isn't fatal if we can't read the table of contents, it * just means that the user won't be able to issue the play tracks * ioctl, and likely lots of other stuff won't work either. They * need to burn the CD before we can do a whole lot with it. So * we don't print anything here if we get an error back. */ error = cdreadtoc(periph, 0, 0, (u_int8_t *)toch, sizeof(*toch), SF_NO_PRINT); /* * Errors in reading the table of contents aren't fatal, we just * won't have a valid table of contents cached. */ if (error != 0) { error = 0; bzero(&softc->toc, sizeof(softc->toc)); goto bailout; } if (softc->quirks & CD_Q_BCD_TRACKS) { toch->starting_track = bcd2bin(toch->starting_track); toch->ending_track = bcd2bin(toch->ending_track); } /* Number of TOC entries, plus leadout */ num_entries = (toch->ending_track - toch->starting_track) + 2; if (num_entries <= 0) goto bailout; toclen = num_entries * sizeof(struct cd_toc_entry); error = cdreadtoc(periph, CD_MSF_FORMAT, toch->starting_track, (u_int8_t *)&softc->toc, toclen + sizeof(*toch), SF_NO_PRINT); if (error != 0) { error = 0; bzero(&softc->toc, sizeof(softc->toc)); goto bailout; } if (softc->quirks & CD_Q_BCD_TRACKS) { toch->starting_track = bcd2bin(toch->starting_track); toch->ending_track = bcd2bin(toch->ending_track); } /* * XXX KDM is this necessary? Probably only if the drive doesn't * return leadout information with the table of contents. */ cdindex = toch->starting_track + num_entries -1; if (cdindex == toch->ending_track + 1) { error = cdreadtoc(periph, CD_MSF_FORMAT, LEADOUT, (u_int8_t *)&leadout, sizeof(leadout), SF_NO_PRINT); if (error != 0) { error = 0; goto bailout; } softc->toc.entries[cdindex - toch->starting_track] = leadout.entry; } if (softc->quirks & CD_Q_BCD_TRACKS) { for (cdindex = 0; cdindex < num_entries - 1; cdindex++) { softc->toc.entries[cdindex].track = bcd2bin(softc->toc.entries[cdindex].track); } } softc->flags |= CD_FLAG_VALID_TOC; /* If the first track is audio, correct sector size. */ if ((softc->toc.entries[0].control & 4) == 0) { softc->disk->d_sectorsize = softc->params.blksize = 2352; softc->disk->d_mediasize = (off_t)softc->params.blksize * softc->params.disksize; } bailout: /* * We unconditionally (re)set the blocksize each time the * CD device is opened. This is because the CD can change, * and therefore the blocksize might change. * XXX problems here if some slice or partition is still * open with the old size? */ if ((softc->disk->d_devstat->flags & DEVSTAT_BS_UNAVAILABLE) != 0) softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE; softc->disk->d_devstat->block_size = softc->params.blksize; return (error); } static int cdsize(struct cam_periph *periph, u_int32_t *size) { struct cd_softc *softc; union ccb *ccb; struct scsi_read_capacity_data *rcap_buf; int error; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n")); softc = (struct cd_softc *)periph->softc; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); /* XXX Should be M_WAITOK */ rcap_buf = malloc(sizeof(struct scsi_read_capacity_data), M_SCSICD, M_NOWAIT | M_ZERO); if (rcap_buf == NULL) return (ENOMEM); scsi_read_capacity(&ccb->csio, /*retries*/ cd_retry_count, cddone, MSG_SIMPLE_Q_TAG, rcap_buf, SSD_FULL_SIZE, /* timeout */20000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); xpt_release_ccb(ccb); softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1; softc->params.blksize = scsi_4btoul(rcap_buf->length); /* Make sure we got at least some block size. */ if (error == 0 && softc->params.blksize == 0) error = EIO; /* * SCSI-3 mandates that the reported blocksize shall be 2048. * Older drives sometimes report funny values, trim it down to * 2048, or other parts of the kernel will get confused. * * XXX we leave drives alone that might report 512 bytes, as * well as drives reporting more weird sizes like perhaps 4K. */ if (softc->params.blksize > 2048 && softc->params.blksize <= 2352) softc->params.blksize = 2048; free(rcap_buf, M_SCSICD); *size = softc->params.disksize; return (error); } static int cd6byteworkaround(union ccb *ccb) { u_int8_t *cdb; struct cam_periph *periph; struct cd_softc *softc; struct cd_mode_params *params; int frozen, found; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; cdb = ccb->csio.cdb_io.cdb_bytes; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) || ((cdb[0] != MODE_SENSE_6) && (cdb[0] != MODE_SELECT_6))) return (0); /* * Because there is no convenient place to stash the overall * cd_mode_params structure pointer, we have to grab it like this. * This means that ALL MODE_SENSE and MODE_SELECT requests in the * cd(4) driver MUST go through cdgetmode() and cdsetmode()! * * XXX It would be nice if, at some point, we could increase the * number of available peripheral private pointers. Both pointers * are currently used in most every peripheral driver. */ found = 0; STAILQ_FOREACH(params, &softc->mode_queue, links) { if (params->mode_buf == ccb->csio.data_ptr) { found = 1; break; } } /* * This shouldn't happen. All mode sense and mode select * operations in the cd(4) driver MUST go through cdgetmode() and * cdsetmode()! */ if (found == 0) { xpt_print(periph->path, "mode buffer not found in mode queue!\n"); return (0); } params->cdb_size = 10; softc->minimum_command_size = 10; xpt_print(ccb->ccb_h.path, "%s(6) failed, increasing minimum CDB size to 10 bytes\n", (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT"); if (cdb[0] == MODE_SENSE_6) { struct scsi_mode_sense_10 ms10; struct scsi_mode_sense_6 *ms6; int len; ms6 = (struct scsi_mode_sense_6 *)cdb; bzero(&ms10, sizeof(ms10)); ms10.opcode = MODE_SENSE_10; ms10.byte2 = ms6->byte2; ms10.page = ms6->page; /* * 10 byte mode header, block descriptor, * sizeof(union cd_pages) */ len = sizeof(struct cd_mode_data_10); ccb->csio.dxfer_len = len; scsi_ulto2b(len, ms10.length); ms10.control = ms6->control; bcopy(&ms10, cdb, 10); ccb->csio.cdb_len = 10; } else { struct scsi_mode_select_10 ms10; struct scsi_mode_select_6 *ms6; struct scsi_mode_header_6 *header6; struct scsi_mode_header_10 *header10; struct scsi_mode_page_header *page_header; int blk_desc_len, page_num, page_size, len; ms6 = (struct scsi_mode_select_6 *)cdb; bzero(&ms10, sizeof(ms10)); ms10.opcode = MODE_SELECT_10; ms10.byte2 = ms6->byte2; header6 = (struct scsi_mode_header_6 *)params->mode_buf; header10 = (struct scsi_mode_header_10 *)params->mode_buf; page_header = find_mode_page_6(header6); page_num = page_header->page_code; blk_desc_len = header6->blk_desc_len; page_size = cdgetpagesize(page_num); if (page_size != (page_header->page_length + sizeof(*page_header))) page_size = page_header->page_length + sizeof(*page_header); len = sizeof(*header10) + blk_desc_len + page_size; len = min(params->alloc_len, len); /* * Since the 6 byte parameter header is shorter than the 10 * byte parameter header, we need to copy the actual mode * page data, and the block descriptor, if any, so things wind * up in the right place. The regions will overlap, but * bcopy() does the right thing. */ bcopy(params->mode_buf + sizeof(*header6), params->mode_buf + sizeof(*header10), len - sizeof(*header10)); /* Make sure these fields are set correctly. */ scsi_ulto2b(0, header10->data_length); header10->medium_type = 0; scsi_ulto2b(blk_desc_len, header10->blk_desc_len); ccb->csio.dxfer_len = len; scsi_ulto2b(len, ms10.length); ms10.control = ms6->control; bcopy(&ms10, cdb, 10); ccb->csio.cdb_len = 10; } frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_action(ccb); if (frozen) { cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*openings*/0, /*timeout*/0, /*getcount_only*/0); } return (ERESTART); } static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; int error, error_code, sense_key, asc, ascq; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; error = 0; /* * We use a status of CAM_REQ_INVALID as shorthand -- if a 6 byte * CDB comes back with this particular error, try transforming it * into the 10 byte version. */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { error = cd6byteworkaround(ccb); } else if (scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq)) { if (sense_key == SSD_KEY_ILLEGAL_REQUEST) error = cd6byteworkaround(ccb); else if (sense_key == SSD_KEY_UNIT_ATTENTION && asc == 0x28 && ascq == 0x00) disk_media_changed(softc->disk, M_NOWAIT); else if (sense_key == SSD_KEY_NOT_READY && asc == 0x3a && (softc->flags & CD_FLAG_SAW_MEDIA)) { softc->flags &= ~CD_FLAG_SAW_MEDIA; disk_media_gone(softc->disk, M_NOWAIT); } } if (error == ERESTART) return (error); /* * XXX * Until we have a better way of doing pack validation, * don't treat UAs as errors. */ sense_flags |= SF_RETRY_UA; + + if (softc->quirks & CD_Q_RETRY_BUSY) + sense_flags |= SF_RETRY_BUSY; return (cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } static void cdmediapoll(void *arg) { struct cam_periph *periph = arg; struct cd_softc *softc = periph->softc; if (softc->state == CD_STATE_NORMAL && !softc->tur && softc->outstanding_cmds == 0) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { softc->tur = 1; xpt_schedule(periph, CAM_PRIORITY_NORMAL); } } /* Queue us up again */ if (cd_poll_period != 0) callout_schedule(&softc->mediapoll_c, cd_poll_period * hz); } /* * Read table of contents */ static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, u_int8_t *data, u_int32_t len, u_int32_t sense_flags) { struct scsi_read_toc *scsi_cmd; u_int32_t ntoc; struct ccb_scsiio *csio; union ccb *ccb; int error; ntoc = len; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_IN, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ data, /* dxfer_len */ len, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_read_toc), /* timeout */ 50000); scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); if (mode == CD_MSF_FORMAT) scsi_cmd->byte2 |= CD_MSF; scsi_cmd->from_track = start; /* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */ scsi_cmd->data_len[0] = (ntoc) >> 8; scsi_cmd->data_len[1] = (ntoc) & 0xff; scsi_cmd->op_code = READ_TOC; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA | sense_flags); xpt_release_ccb(ccb); return(error); } static int cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, u_int32_t format, int track, struct cd_sub_channel_info *data, u_int32_t len) { struct scsi_read_subchannel *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_IN, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ (u_int8_t *)data, /* dxfer_len */ len, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_read_subchannel), /* timeout */ 50000); scsi_cmd = (struct scsi_read_subchannel *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = READ_SUBCHANNEL; if (mode == CD_MSF_FORMAT) scsi_cmd->byte1 |= CD_MSF; scsi_cmd->byte2 = SRS_SUBQ; scsi_cmd->subchan_format = format; scsi_cmd->track = track; scsi_ulto2b(len, (u_int8_t *)scsi_cmd->data_len); scsi_cmd->control = 0; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } /* * All MODE_SENSE requests in the cd(4) driver MUST go through this * routine. See comments in cd6byteworkaround() for details. */ static int cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, u_int32_t page) { struct ccb_scsiio *csio; struct cd_softc *softc; union ccb *ccb; int param_len; int error; softc = (struct cd_softc *)periph->softc; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; data->cdb_size = softc->minimum_command_size; if (data->cdb_size < 10) param_len = sizeof(struct cd_mode_data); else param_len = sizeof(struct cd_mode_data_10); /* Don't say we've got more room than we actually allocated */ param_len = min(param_len, data->alloc_len); scsi_mode_sense_len(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* dbd */ 0, /* page_code */ SMS_PAGE_CTRL_CURRENT, /* page */ page, /* param_buf */ data->mode_buf, /* param_len */ param_len, /* minimum_cmd_size */ softc->minimum_command_size, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); /* * It would be nice not to have to do this, but there's no * available pointer in the CCB that would allow us to stuff the * mode params structure in there and retrieve it in * cd6byteworkaround(), so we can set the cdb size. The cdb size * lets the caller know what CDB size we ended up using, so they * can find the actual mode page offset. */ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); /* * This is a bit of belt-and-suspenders checking, but if we run * into a situation where the target sends back multiple block * descriptors, we might not have enough space in the buffer to * see the whole mode page. Better to return an error than * potentially access memory beyond our malloced region. */ if (error == 0) { u_int32_t data_len; if (data->cdb_size == 10) { struct scsi_mode_header_10 *hdr10; hdr10 = (struct scsi_mode_header_10 *)data->mode_buf; data_len = scsi_2btoul(hdr10->data_length); data_len += sizeof(hdr10->data_length); } else { struct scsi_mode_header_6 *hdr6; hdr6 = (struct scsi_mode_header_6 *)data->mode_buf; data_len = hdr6->data_length; data_len += sizeof(hdr6->data_length); } /* * Complain if there is more mode data available than we * allocated space for. This could potentially happen if * we miscalculated the page length for some reason, if the * drive returns multiple block descriptors, or if it sets * the data length incorrectly. */ if (data_len > data->alloc_len) { xpt_print(periph->path, "allocated modepage %d length " "%d < returned length %d\n", page, data->alloc_len, data_len); error = ENOSPC; } } return (error); } /* * All MODE_SELECT requests in the cd(4) driver MUST go through this * routine. See comments in cd6byteworkaround() for details. */ static int cdsetmode(struct cam_periph *periph, struct cd_mode_params *data) { struct ccb_scsiio *csio; struct cd_softc *softc; union ccb *ccb; int cdb_size, param_len; int error; softc = (struct cd_softc *)periph->softc; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; error = 0; /* * If the data is formatted for the 10 byte version of the mode * select parameter list, we need to use the 10 byte CDB. * Otherwise, we use whatever the stored minimum command size. */ if (data->cdb_size == 10) cdb_size = data->cdb_size; else cdb_size = softc->minimum_command_size; if (cdb_size >= 10) { struct scsi_mode_header_10 *mode_header; u_int32_t data_len; mode_header = (struct scsi_mode_header_10 *)data->mode_buf; data_len = scsi_2btoul(mode_header->data_length); scsi_ulto2b(0, mode_header->data_length); /* * SONY drives do not allow a mode select with a medium_type * value that has just been returned by a mode sense; use a * medium_type of 0 (Default) instead. */ mode_header->medium_type = 0; /* * Pass back whatever the drive passed to us, plus the size * of the data length field. */ param_len = data_len + sizeof(mode_header->data_length); } else { struct scsi_mode_header_6 *mode_header; mode_header = (struct scsi_mode_header_6 *)data->mode_buf; param_len = mode_header->data_length + 1; mode_header->data_length = 0; /* * SONY drives do not allow a mode select with a medium_type * value that has just been returned by a mode sense; use a * medium_type of 0 (Default) instead. */ mode_header->medium_type = 0; } /* Don't say we've got more room than we actually allocated */ param_len = min(param_len, data->alloc_len); scsi_mode_select_len(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* scsi_page_fmt */ 1, /* save_pages */ 0, /* param_buf */ data->mode_buf, /* param_len */ param_len, /* minimum_cmd_size */ cdb_size, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); /* See comments in cdgetmode() and cd6byteworkaround(). */ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); return (error); } static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len) { struct ccb_scsiio *csio; union ccb *ccb; int error; u_int8_t cdb_len; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; /* * Use the smallest possible command to perform the operation. */ if ((len & 0xffff0000) == 0) { /* * We can fit in a 10 byte cdb. */ struct scsi_play_10 *scsi_cmd; scsi_cmd = (struct scsi_play_10 *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_10; scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); scsi_ulto2b(len, (u_int8_t *)scsi_cmd->xfer_len); cdb_len = sizeof(*scsi_cmd); } else { struct scsi_play_12 *scsi_cmd; scsi_cmd = (struct scsi_play_12 *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_12; scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); scsi_ulto4b(len, (u_int8_t *)scsi_cmd->xfer_len); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, /*retries*/ cd_retry_count, cddone, /*flags*/CAM_DIR_NONE, MSG_SIMPLE_Q_TAG, /*dataptr*/NULL, /*datalen*/0, /*sense_len*/SSD_FULL_SIZE, cdb_len, /*timeout*/50 * 1000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf) { struct scsi_play_msf *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_play_msf), /* timeout */ 50000); scsi_cmd = (struct scsi_play_msf *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_MSF; scsi_cmd->start_m = startm; scsi_cmd->start_s = starts; scsi_cmd->start_f = startf; scsi_cmd->end_m = endm; scsi_cmd->end_s = ends; scsi_cmd->end_f = endf; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, u_int32_t etrack, u_int32_t eindex) { struct scsi_play_track *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_play_track), /* timeout */ 50000); scsi_cmd = (struct scsi_play_track *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_TRACK; scsi_cmd->start_track = strack; scsi_cmd->start_index = sindex; scsi_cmd->end_track = etrack; scsi_cmd->end_index = eindex; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdpause(struct cam_periph *periph, u_int32_t go) { struct scsi_pause *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_pause), /* timeout */ 50000); scsi_cmd = (struct scsi_pause *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PAUSE; scsi_cmd->resume = go; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdstartunit(struct cam_periph *periph, int load) { union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_start_stop(&ccb->csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* start */ TRUE, /* load_eject */ load, /* immediate */ FALSE, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdstopunit(struct cam_periph *periph, u_int32_t eject) { union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_start_stop(&ccb->csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* start */ FALSE, /* load_eject */ eject, /* immediate */ FALSE, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed) { struct scsi_set_speed *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; /* Preserve old behavior: units in multiples of CDROM speed */ if (rdspeed < 177) rdspeed *= 177; if (wrspeed < 177) wrspeed *= 177; cam_fill_csio(csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_set_speed), /* timeout */ 50000); scsi_cmd = (struct scsi_set_speed *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SET_CD_SPEED; scsi_ulto2b(rdspeed, scsi_cmd->readspeed); scsi_ulto2b(wrspeed, scsi_cmd->writespeed); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) { union ccb *ccb; u_int8_t *databuf; u_int32_t lba; int error; int length; error = 0; databuf = NULL; lba = 0; switch (authinfo->format) { case DVD_REPORT_AGID: length = sizeof(struct scsi_report_key_data_agid); break; case DVD_REPORT_CHALLENGE: length = sizeof(struct scsi_report_key_data_challenge); break; case DVD_REPORT_KEY1: length = sizeof(struct scsi_report_key_data_key1_key2); break; case DVD_REPORT_TITLE_KEY: length = sizeof(struct scsi_report_key_data_title); /* The lba field is only set for the title key */ lba = authinfo->lba; break; case DVD_REPORT_ASF: length = sizeof(struct scsi_report_key_data_asf); break; case DVD_REPORT_RPC: length = sizeof(struct scsi_report_key_data_rpc); break; case DVD_INVALIDATE_AGID: length = 0; break; default: return (EINVAL); } if (length != 0) { databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); } else databuf = NULL; cam_periph_lock(periph); ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_report_key(&ccb->csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* lba */ lba, /* agid */ authinfo->agid, /* key_format */ authinfo->format, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); if (error != 0) goto bailout; if (ccb->csio.resid != 0) { xpt_print(periph->path, "warning, residual for report key " "command is %d\n", ccb->csio.resid); } switch(authinfo->format) { case DVD_REPORT_AGID: { struct scsi_report_key_data_agid *agid_data; agid_data = (struct scsi_report_key_data_agid *)databuf; authinfo->agid = (agid_data->agid & RKD_AGID_MASK) >> RKD_AGID_SHIFT; break; } case DVD_REPORT_CHALLENGE: { struct scsi_report_key_data_challenge *chal_data; chal_data = (struct scsi_report_key_data_challenge *)databuf; bcopy(chal_data->challenge_key, authinfo->keychal, min(sizeof(chal_data->challenge_key), sizeof(authinfo->keychal))); break; } case DVD_REPORT_KEY1: { struct scsi_report_key_data_key1_key2 *key1_data; key1_data = (struct scsi_report_key_data_key1_key2 *)databuf; bcopy(key1_data->key1, authinfo->keychal, min(sizeof(key1_data->key1), sizeof(authinfo->keychal))); break; } case DVD_REPORT_TITLE_KEY: { struct scsi_report_key_data_title *title_data; title_data = (struct scsi_report_key_data_title *)databuf; authinfo->cpm = (title_data->byte0 & RKD_TITLE_CPM) >> RKD_TITLE_CPM_SHIFT; authinfo->cp_sec = (title_data->byte0 & RKD_TITLE_CP_SEC) >> RKD_TITLE_CP_SEC_SHIFT; authinfo->cgms = (title_data->byte0 & RKD_TITLE_CMGS_MASK) >> RKD_TITLE_CMGS_SHIFT; bcopy(title_data->title_key, authinfo->keychal, min(sizeof(title_data->title_key), sizeof(authinfo->keychal))); break; } case DVD_REPORT_ASF: { struct scsi_report_key_data_asf *asf_data; asf_data = (struct scsi_report_key_data_asf *)databuf; authinfo->asf = asf_data->success & RKD_ASF_SUCCESS; break; } case DVD_REPORT_RPC: { struct scsi_report_key_data_rpc *rpc_data; rpc_data = (struct scsi_report_key_data_rpc *)databuf; authinfo->reg_type = (rpc_data->byte4 & RKD_RPC_TYPE_MASK) >> RKD_RPC_TYPE_SHIFT; authinfo->vend_rsts = (rpc_data->byte4 & RKD_RPC_VENDOR_RESET_MASK) >> RKD_RPC_VENDOR_RESET_SHIFT; authinfo->user_rsts = rpc_data->byte4 & RKD_RPC_USER_RESET_MASK; authinfo->region = rpc_data->region_mask; authinfo->rpc_scheme = rpc_data->rpc_scheme1; break; } case DVD_INVALIDATE_AGID: break; default: /* This should be impossible, since we checked above */ error = EINVAL; goto bailout; break; /* NOTREACHED */ } bailout: xpt_release_ccb(ccb); cam_periph_unlock(periph); if (databuf != NULL) free(databuf, M_DEVBUF); return(error); } static int cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) { union ccb *ccb; u_int8_t *databuf; int length; int error; error = 0; databuf = NULL; switch(authinfo->format) { case DVD_SEND_CHALLENGE: { struct scsi_report_key_data_challenge *challenge_data; length = sizeof(*challenge_data); challenge_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)challenge_data; scsi_ulto2b(length - sizeof(challenge_data->data_len), challenge_data->data_len); bcopy(authinfo->keychal, challenge_data->challenge_key, min(sizeof(authinfo->keychal), sizeof(challenge_data->challenge_key))); break; } case DVD_SEND_KEY2: { struct scsi_report_key_data_key1_key2 *key2_data; length = sizeof(*key2_data); key2_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)key2_data; scsi_ulto2b(length - sizeof(key2_data->data_len), key2_data->data_len); bcopy(authinfo->keychal, key2_data->key1, min(sizeof(authinfo->keychal), sizeof(key2_data->key1))); break; } case DVD_SEND_RPC: { struct scsi_send_key_data_rpc *rpc_data; length = sizeof(*rpc_data); rpc_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)rpc_data; scsi_ulto2b(length - sizeof(rpc_data->data_len), rpc_data->data_len); rpc_data->region_code = authinfo->region; break; } default: return (EINVAL); } cam_periph_lock(periph); ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_send_key(&ccb->csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* agid */ authinfo->agid, /* key_format */ authinfo->format, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); cam_periph_unlock(periph); if (databuf != NULL) free(databuf, M_DEVBUF); return(error); } static int cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct) { union ccb *ccb; u_int8_t *databuf; u_int32_t address; int error; int length; error = 0; databuf = NULL; /* The address is reserved for many of the formats */ address = 0; switch(dvdstruct->format) { case DVD_STRUCT_PHYSICAL: length = sizeof(struct scsi_read_dvd_struct_data_physical); break; case DVD_STRUCT_COPYRIGHT: length = sizeof(struct scsi_read_dvd_struct_data_copyright); break; case DVD_STRUCT_DISCKEY: length = sizeof(struct scsi_read_dvd_struct_data_disc_key); break; case DVD_STRUCT_BCA: length = sizeof(struct scsi_read_dvd_struct_data_bca); break; case DVD_STRUCT_MANUFACT: length = sizeof(struct scsi_read_dvd_struct_data_manufacturer); break; case DVD_STRUCT_CMI: return (ENODEV); case DVD_STRUCT_PROTDISCID: length = sizeof(struct scsi_read_dvd_struct_data_prot_discid); break; case DVD_STRUCT_DISCKEYBLOCK: length = sizeof(struct scsi_read_dvd_struct_data_disc_key_blk); break; case DVD_STRUCT_DDS: length = sizeof(struct scsi_read_dvd_struct_data_dds); break; case DVD_STRUCT_MEDIUM_STAT: length = sizeof(struct scsi_read_dvd_struct_data_medium_status); break; case DVD_STRUCT_SPARE_AREA: length = sizeof(struct scsi_read_dvd_struct_data_spare_area); break; case DVD_STRUCT_RMD_LAST: return (ENODEV); case DVD_STRUCT_RMD_RMA: return (ENODEV); case DVD_STRUCT_PRERECORDED: length = sizeof(struct scsi_read_dvd_struct_data_leadin); break; case DVD_STRUCT_UNIQUEID: length = sizeof(struct scsi_read_dvd_struct_data_disc_id); break; case DVD_STRUCT_DCB: return (ENODEV); case DVD_STRUCT_LIST: /* * This is the maximum allocation length for the READ DVD * STRUCTURE command. There's nothing in the MMC3 spec * that indicates a limit in the amount of data that can * be returned from this call, other than the limits * imposed by the 2-byte length variables. */ length = 65535; break; default: return (EINVAL); } if (length != 0) { databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); } else databuf = NULL; cam_periph_lock(periph); ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_read_dvd_structure(&ccb->csio, /* retries */ cd_retry_count, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* lba */ address, /* layer_number */ dvdstruct->layer_num, /* key_format */ dvdstruct->format, /* agid */ dvdstruct->agid, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); if (error != 0) goto bailout; switch(dvdstruct->format) { case DVD_STRUCT_PHYSICAL: { struct scsi_read_dvd_struct_data_layer_desc *inlayer; struct dvd_layer *outlayer; struct scsi_read_dvd_struct_data_physical *phys_data; phys_data = (struct scsi_read_dvd_struct_data_physical *)databuf; inlayer = &phys_data->layer_desc; outlayer = (struct dvd_layer *)&dvdstruct->data; dvdstruct->length = sizeof(*inlayer); outlayer->book_type = (inlayer->book_type_version & RDSD_BOOK_TYPE_MASK) >> RDSD_BOOK_TYPE_SHIFT; outlayer->book_version = (inlayer->book_type_version & RDSD_BOOK_VERSION_MASK); outlayer->disc_size = (inlayer->disc_size_max_rate & RDSD_DISC_SIZE_MASK) >> RDSD_DISC_SIZE_SHIFT; outlayer->max_rate = (inlayer->disc_size_max_rate & RDSD_MAX_RATE_MASK); outlayer->nlayers = (inlayer->layer_info & RDSD_NUM_LAYERS_MASK) >> RDSD_NUM_LAYERS_SHIFT; outlayer->track_path = (inlayer->layer_info & RDSD_TRACK_PATH_MASK) >> RDSD_TRACK_PATH_SHIFT; outlayer->layer_type = (inlayer->layer_info & RDSD_LAYER_TYPE_MASK); outlayer->linear_density = (inlayer->density & RDSD_LIN_DENSITY_MASK) >> RDSD_LIN_DENSITY_SHIFT; outlayer->track_density = (inlayer->density & RDSD_TRACK_DENSITY_MASK); outlayer->bca = (inlayer->bca & RDSD_BCA_MASK) >> RDSD_BCA_SHIFT; outlayer->start_sector = scsi_3btoul(inlayer->main_data_start); outlayer->end_sector = scsi_3btoul(inlayer->main_data_end); outlayer->end_sector_l0 = scsi_3btoul(inlayer->end_sector_layer0); break; } case DVD_STRUCT_COPYRIGHT: { struct scsi_read_dvd_struct_data_copyright *copy_data; copy_data = (struct scsi_read_dvd_struct_data_copyright *) databuf; dvdstruct->cpst = copy_data->cps_type; dvdstruct->rmi = copy_data->region_info; dvdstruct->length = 0; break; } default: /* * Tell the user what the overall length is, no matter * what we can actually fit in the data buffer. */ dvdstruct->length = length - ccb->csio.resid - sizeof(struct scsi_read_dvd_struct_data_header); /* * But only actually copy out the smaller of what we read * in or what the structure can take. */ bcopy(databuf + sizeof(struct scsi_read_dvd_struct_data_header), dvdstruct->data, min(sizeof(dvdstruct->data), dvdstruct->length)); break; } bailout: xpt_release_ccb(ccb); cam_periph_unlock(periph); if (databuf != NULL) free(databuf, M_DEVBUF); return(error); } void scsi_report_key(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t lba, u_int8_t agid, u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_report_key *scsi_cmd; scsi_cmd = (struct scsi_report_key *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_KEY; scsi_ulto4b(lba, scsi_cmd->lba); scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | (key_format & RK_KF_KEYFORMAT_MASK); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ (dxfer_len == 0) ? CAM_DIR_NONE : CAM_DIR_IN, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_send_key(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t agid, u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_send_key *scsi_cmd; scsi_cmd = (struct scsi_send_key *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SEND_KEY; scsi_ulto2b(dxfer_len, scsi_cmd->param_len); scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | (key_format & RK_KF_KEYFORMAT_MASK); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ CAM_DIR_OUT, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t address, u_int8_t layer_number, u_int8_t format, u_int8_t agid, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_read_dvd_structure *scsi_cmd; scsi_cmd = (struct scsi_read_dvd_structure *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_DVD_STRUCTURE; scsi_ulto4b(address, scsi_cmd->address); scsi_cmd->layer_number = layer_number; scsi_cmd->format = format; scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); /* The AGID is the top two bits of this byte */ scsi_cmd->agid = agid << 6; cam_fill_csio(csio, retries, cbfcnp, /*flags*/ CAM_DIR_IN, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } Index: projects/building-blocks/sys/cam/scsi/scsi_da.c =================================================================== --- projects/building-blocks/sys/cam/scsi/scsi_da.c (revision 278195) +++ projects/building-blocks/sys/cam/scsi/scsi_da.c (revision 278196) @@ -1,3994 +1,4008 @@ /*- * Implementation of SCSI Direct Access Peripheral driver for CAM. * * Copyright (c) 1997 Justin T. Gibbs. * 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, immediately at the beginning of the file. * 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 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 #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* _KERNEL */ #ifndef _KERNEL #include #include #endif /* _KERNEL */ #include #include #include #include #include #include #ifndef _KERNEL #include #endif /* !_KERNEL */ #ifdef _KERNEL typedef enum { DA_STATE_PROBE_RC, DA_STATE_PROBE_RC16, DA_STATE_PROBE_LBP, DA_STATE_PROBE_BLK_LIMITS, DA_STATE_PROBE_BDC, DA_STATE_PROBE_ATA, DA_STATE_NORMAL } da_state; typedef enum { DA_FLAG_PACK_INVALID = 0x001, DA_FLAG_NEW_PACK = 0x002, DA_FLAG_PACK_LOCKED = 0x004, DA_FLAG_PACK_REMOVABLE = 0x008, DA_FLAG_NEED_OTAG = 0x020, DA_FLAG_WAS_OTAG = 0x040, DA_FLAG_RETRY_UA = 0x080, DA_FLAG_OPEN = 0x100, DA_FLAG_SCTX_INIT = 0x200, DA_FLAG_CAN_RC16 = 0x400, DA_FLAG_PROBED = 0x800, DA_FLAG_DIRTY = 0x1000, DA_FLAG_ANNOUNCED = 0x2000 } da_flags; typedef enum { DA_Q_NONE = 0x00, DA_Q_NO_SYNC_CACHE = 0x01, DA_Q_NO_6_BYTE = 0x02, DA_Q_NO_PREVENT = 0x04, DA_Q_4K = 0x08, DA_Q_NO_RC16 = 0x10, - DA_Q_NO_UNMAP = 0x20 + DA_Q_NO_UNMAP = 0x20, + DA_Q_RETRY_BUSY = 0x40 } da_quirks; #define DA_Q_BIT_STRING \ "\020" \ "\001NO_SYNC_CACHE" \ "\002NO_6_BYTE" \ "\003NO_PREVENT" \ "\0044K" \ - "\005NO_RC16" + "\005NO_RC16" \ + "\006NO_UNMAP" \ + "\007RETRY_BUSY" typedef enum { DA_CCB_PROBE_RC = 0x01, DA_CCB_PROBE_RC16 = 0x02, DA_CCB_PROBE_LBP = 0x03, DA_CCB_PROBE_BLK_LIMITS = 0x04, DA_CCB_PROBE_BDC = 0x05, DA_CCB_PROBE_ATA = 0x06, DA_CCB_BUFFER_IO = 0x07, DA_CCB_DUMP = 0x0A, DA_CCB_DELETE = 0x0B, DA_CCB_TUR = 0x0C, DA_CCB_TYPE_MASK = 0x0F, DA_CCB_RETRY_UA = 0x10 } da_ccb_state; /* * Order here is important for method choice * * We prefer ATA_TRIM as tests run against a Sandforce 2281 SSD attached to * LSI 2008 (mps) controller (FW: v12, Drv: v14) resulted 20% quicker deletes * using ATA_TRIM than the corresponding UNMAP results for a real world mysql * import taking 5mins. * */ typedef enum { DA_DELETE_NONE, DA_DELETE_DISABLE, DA_DELETE_ATA_TRIM, DA_DELETE_UNMAP, DA_DELETE_WS16, DA_DELETE_WS10, DA_DELETE_ZERO, DA_DELETE_MIN = DA_DELETE_ATA_TRIM, DA_DELETE_MAX = DA_DELETE_ZERO } da_delete_methods; typedef void da_delete_func_t (struct cam_periph *periph, union ccb *ccb, struct bio *bp); static da_delete_func_t da_delete_trim; static da_delete_func_t da_delete_unmap; static da_delete_func_t da_delete_ws; static const void * da_delete_functions[] = { NULL, NULL, da_delete_trim, da_delete_unmap, da_delete_ws, da_delete_ws, da_delete_ws }; static const char *da_delete_method_names[] = { "NONE", "DISABLE", "ATA_TRIM", "UNMAP", "WS16", "WS10", "ZERO" }; static const char *da_delete_method_desc[] = { "NONE", "DISABLED", "ATA TRIM", "UNMAP", "WRITE SAME(16) with UNMAP", "WRITE SAME(10) with UNMAP", "ZERO" }; /* Offsets into our private area for storing information */ #define ccb_state ppriv_field0 #define ccb_bp ppriv_ptr1 struct disk_params { u_int8_t heads; u_int32_t cylinders; u_int8_t secs_per_track; u_int32_t secsize; /* Number of bytes/sector */ u_int64_t sectors; /* total number sectors */ u_int stripesize; u_int stripeoffset; }; #define UNMAP_RANGE_MAX 0xffffffff #define UNMAP_HEAD_SIZE 8 #define UNMAP_RANGE_SIZE 16 #define UNMAP_MAX_RANGES 2048 /* Protocol Max is 4095 */ #define UNMAP_BUF_SIZE ((UNMAP_MAX_RANGES * UNMAP_RANGE_SIZE) + \ UNMAP_HEAD_SIZE) #define WS10_MAX_BLKS 0xffff #define WS16_MAX_BLKS 0xffffffff #define ATA_TRIM_MAX_RANGES ((UNMAP_BUF_SIZE / \ (ATA_DSM_RANGE_SIZE * ATA_DSM_BLK_SIZE)) * ATA_DSM_BLK_SIZE) struct da_softc { struct bio_queue_head bio_queue; struct bio_queue_head delete_queue; struct bio_queue_head delete_run_queue; LIST_HEAD(, ccb_hdr) pending_ccbs; int tur; /* TEST UNIT READY should be sent */ int refcount; /* Active xpt_action() calls */ da_state state; da_flags flags; da_quirks quirks; int sort_io_queue; int minimum_cmd_size; int error_inject; int trim_max_ranges; int delete_running; int delete_available; /* Delete methods possibly available */ u_int maxio; uint32_t unmap_max_ranges; uint32_t unmap_max_lba; /* Max LBAs in UNMAP req */ uint64_t ws_max_blks; da_delete_methods delete_method; da_delete_func_t *delete_func; struct disk_params params; struct disk *disk; union ccb saved_ccb; struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; struct callout sendordered_c; uint64_t wwpn; uint8_t unmap_buf[UNMAP_BUF_SIZE]; struct scsi_read_capacity_data_long rcaplong; struct callout mediapoll_c; }; #define dadeleteflag(softc, delete_method, enable) \ if (enable) { \ softc->delete_available |= (1 << delete_method); \ } else { \ softc->delete_available &= ~(1 << delete_method); \ } struct da_quirk_entry { struct scsi_inquiry_pattern inq_pat; da_quirks quirks; }; static const char quantum[] = "QUANTUM"; static const char microp[] = "MICROP"; static struct da_quirk_entry da_quirk_table[] = { /* SPI, FC devices */ { /* * Fujitsu M2513A MO drives. * Tested devices: M2513A2 firmware versions 1200 & 1300. * (dip switch selects whether T_DIRECT or T_OPTICAL device) * Reported by: W.Scholten */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* See above. */ {T_OPTICAL, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This particular Fujitsu drive doesn't like the * synchronize cache command. * Reported by: Tom Jackson */ {T_DIRECT, SIP_MEDIA_FIXED, "FUJITSU", "M2954*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This drive doesn't like the synchronize cache command * either. Reported by: Matthew Jacob * in NetBSD PR kern/6027, August 24, 1998. */ {T_DIRECT, SIP_MEDIA_FIXED, microp, "2217*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This drive doesn't like the synchronize cache command * either. Reported by: Hellmuth Michaelis (hm@kts.org) * (PR 8882). */ {T_DIRECT, SIP_MEDIA_FIXED, microp, "2112*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: Blaz Zupan */ {T_DIRECT, SIP_MEDIA_FIXED, "NEC", "D3847*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: Blaz Zupan */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "MAVERICK 540S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS525S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: walter@pelissero.de */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS540S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't work correctly with 6 byte reads/writes. * Returns illegal request, and points to byte 9 of the * 6-byte CDB. * Reported by: Adam McDougall */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 4*", "*"}, /*quirks*/ DA_Q_NO_6_BYTE }, { /* See above. */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 2*", "*"}, /*quirks*/ DA_Q_NO_6_BYTE }, { /* * Doesn't like the synchronize cache command. * Reported by: walter@pelissero.de */ {T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CP3500*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * The CISS RAID controllers do not support SYNC_CACHE */ {T_DIRECT, SIP_MEDIA_FIXED, "COMPAQ", "RAID*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * The STEC SSDs sometimes hang on UNMAP. */ {T_DIRECT, SIP_MEDIA_FIXED, "STEC", "*", "*"}, /*quirks*/ DA_Q_NO_UNMAP }, + { + /* + * VMware returns BUSY status when storage has transient + * connectivity problems, so better wait. + */ + {T_DIRECT, SIP_MEDIA_FIXED, "VMware", "Virtual disk", "*"}, + /*quirks*/ DA_Q_RETRY_BUSY + }, /* USB mass storage devices supported by umass(4) */ { /* * EXATELECOM (Sigmatel) i-Bead 100/105 USB Flash MP3 Player * PR: kern/51675 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "EXATEL", "i-BEAD10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Power Quotient Int. (PQI) USB flash key * PR: kern/53067 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "USB Flash Disk*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Creative Nomad MUVO mp3 player (USB) * PR: kern/53094 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CREATIVE", "NOMAD_MUVO", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * Jungsoft NEXDISK USB flash key * PR: kern/54737 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "JUNGSOFT", "NEXDISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * FreeDik USB Mini Data Drive * PR: kern/54786 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FreeDik*", "Mini Data Drive", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Sigmatel USB Flash MP3 Player * PR: kern/57046 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SigmaTel", "MSCN", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * Neuros USB Digital Audio Computer * PR: kern/63645 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "NEUROS", "dig. audio comp.", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SEAGRAND NP-900 MP3 Player * PR: kern/64563 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SEAGRAND", "NP-900*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * iRiver iFP MP3 player (with UMS Firmware) * PR: kern/54881, i386/63941, kern/66124 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iRiver", "iFP*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Frontier Labs NEX IA+ Digital Audio Player, rev 1.10/0.01 * PR: kern/70158 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FL" , "Nex*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * ZICPlay USB MP3 Player with FM * PR: kern/75057 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "ACTIONS*" , "USB DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * TEAC USB floppy mechanisms */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "TEAC" , "FD-05*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Kingston DataTraveler II+ USB Pen-Drive. * Reported by: Pawel Jakub Dawidek */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Kingston" , "DataTraveler II+", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * USB DISK Pro PMAP * Reported by: jhs * PR: usb/96381 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, " ", "USB DISK Pro", "PMAP"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Motorola E398 Mobile Phone (TransFlash memory card). * Reported by: Wojciech A. Koszek * PR: usb/89889 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Motorola" , "Motorola Phone", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Qware BeatZkey! Pro * PR: usb/79164 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "GENERIC", "USB DISK DEVICE", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Time DPA20B 1GB MP3 Player * PR: usb/81846 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB2.0*", "(FS) FLASH DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Samsung USB key 128Mb * PR: usb/90081 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB-DISK", "FreeDik-FlashUsb", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Kingston DataTraveler 2.0 USB Flash memory. * PR: usb/89196 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Kingston", "DataTraveler 2.0", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Creative MUVO Slim mp3 player (USB) * PR: usb/86131 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CREATIVE", "MuVo Slim", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * United MP5512 Portable MP3 Player (2-in-1 USB DISK/MP3) * PR: usb/80487 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "MUSIC DISK", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SanDisk Micro Cruzer 128MB * PR: usb/75970 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SanDisk" , "Micro Cruzer", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * TOSHIBA TransMemory USB sticks * PR: kern/94660 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "TOSHIBA", "TransMemory", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * PNY USB 3.0 Flash Drives */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "PNY", "USB 3.0 FD*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE | DA_Q_NO_RC16 }, { /* * PNY USB Flash keys * PR: usb/75578, usb/72344, usb/65436 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "*" , "USB DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Genesys 6-in-1 Card Reader * PR: usb/94647 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "STORAGE DEVICE*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Rekam Digital CAMERA * PR: usb/98713 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CAMERA*", "4MP-9J6*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * iRiver H10 MP3 player * PR: usb/102547 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iriver", "H10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * iRiver U10 MP3 player * PR: usb/92306 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iriver", "U10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * X-Micro Flash Disk * PR: usb/96901 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "X-Micro", "Flash Disk", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * EasyMP3 EM732X USB 2.0 Flash MP3 Player * PR: usb/96546 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "EM732X", "MP3 Player*", "1.00"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Denver MP3 player * PR: usb/107101 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "DENVER", "MP3 PLAYER", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Philips USB Key Audio KEY013 * PR: usb/68412 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "PHILIPS", "Key*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE | DA_Q_NO_PREVENT }, { /* * JNC MP3 Player * PR: usb/94439 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "JNC*" , "MP3 Player*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SAMSUNG MP0402H * PR: usb/108427 */ {T_DIRECT, SIP_MEDIA_FIXED, "SAMSUNG", "MP0402H", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * I/O Magic USB flash - Giga Bank * PR: usb/108810 */ {T_DIRECT, SIP_MEDIA_FIXED, "GS-Magic", "stor*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * JoyFly 128mb USB Flash Drive * PR: 96133 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB 2.0", "Flash Disk*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * ChipsBnk usb stick * PR: 103702 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "ChipsBnk", "USB*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Storcase (Kingston) InfoStation IFS FC2/SATA-R 201A * PR: 129858 */ {T_DIRECT, SIP_MEDIA_FIXED, "IFS", "FC2/SATA-R*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Samsung YP-U3 mp3-player * PR: 125398 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Samsung", "YP-U3", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { {T_DIRECT, SIP_MEDIA_REMOVABLE, "Netac", "OnlyDisk*", "2000"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Sony Cyber-Shot DSC cameras * PR: usb/137035 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Sony", "Sony DSC", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE | DA_Q_NO_PREVENT }, { {T_DIRECT, SIP_MEDIA_REMOVABLE, "Kingston", "DataTraveler G3", "1.00"}, /*quirks*/ DA_Q_NO_PREVENT }, { /* At least several Transcent USB sticks lie on RC16. */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "JetFlash", "Transcend*", "*"}, /*quirks*/ DA_Q_NO_RC16 }, /* ATA/SATA devices over SAS/USB/... */ { /* Hitachi Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "Hitachi", "H??????????E3*", "*" }, /*quirks*/DA_Q_4K }, { /* Samsung Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG HD155UI*", "*" }, /*quirks*/DA_Q_4K }, { /* Samsung Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "SAMSUNG", "HD155UI*", "*" }, /*quirks*/DA_Q_4K }, { /* Samsung Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG HD204UI*", "*" }, /*quirks*/DA_Q_4K }, { /* Samsung Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "SAMSUNG", "HD204UI*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST????DL*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST????DL", "*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST???DM*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST???DM*", "*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST????DM*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Barracuda Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST????DM", "*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9500423AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST950042", "3AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9500424AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST950042", "4AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9640423AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST964042", "3AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9640424AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST964042", "4AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9750420AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST975042", "0AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9750422AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST975042", "2AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST9750423AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST975042", "3AS*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Thin Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "ST???LT*", "*" }, /*quirks*/DA_Q_4K }, { /* Seagate Momentus Thin Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ST???LT*", "*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD????RS*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "??RS*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD????RX*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "??RX*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD??????RS*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "????RS*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD??????RX*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Caviar Green Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "????RX*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Black Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD???PKT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Black Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "?PKT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Black Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD?????PKT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Black Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "???PKT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Blue Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD???PVT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Blue Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "?PVT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Blue Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "WDC WD?????PVT*", "*" }, /*quirks*/DA_Q_4K }, { /* WDC Scorpio Blue Advanced Format (4k) drives */ { T_DIRECT, SIP_MEDIA_FIXED, "WDC WD??", "???PVT*", "*" }, /*quirks*/DA_Q_4K }, { /* * Olympus FE-210 camera */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "OLYMPUS", "FE210*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * LG UP3S MP3 player */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "LG", "UP3S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Laser MP3-2GA13 MP3 player */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB 2.0", "(HS) Flash Disk", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * LaCie external 250GB Hard drive des by Porsche * Submitted by: Ben Stuyts * PR: 121474 */ {T_DIRECT, SIP_MEDIA_FIXED, "SAMSUNG", "HM250JI", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, /* SATA SSDs */ { /* * Corsair Force 2 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "Corsair CSSD-F*", "*" }, /*quirks*/DA_Q_4K }, { /* * Corsair Force 3 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "Corsair Force 3*", "*" }, /*quirks*/DA_Q_4K }, { /* * Corsair Neutron GTX SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "Corsair Neutron GTX*", "*" }, /*quirks*/DA_Q_4K }, { /* * Corsair Force GT & GS SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "Corsair Force G*", "*" }, /*quirks*/DA_Q_4K }, { /* * Crucial M4 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "M4-CT???M4SSD2*", "*" }, /*quirks*/DA_Q_4K }, { /* * Crucial RealSSD C300 SSDs * 4k optimised */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "C300-CTFDDAC???MAG*", "*" }, /*quirks*/DA_Q_4K }, { /* * Intel 320 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "INTEL SSDSA2CW*", "*" }, /*quirks*/DA_Q_4K }, { /* * Intel 330 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "INTEL SSDSC2CT*", "*" }, /*quirks*/DA_Q_4K }, { /* * Intel 510 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "INTEL SSDSC2MH*", "*" }, /*quirks*/DA_Q_4K }, { /* * Intel 520 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "INTEL SSDSC2BW*", "*" }, /*quirks*/DA_Q_4K }, { /* * Intel X25-M Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "INTEL SSDSA2M*", "*" }, /*quirks*/DA_Q_4K }, { /* * Kingston E100 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "KINGSTON SE100S3*", "*" }, /*quirks*/DA_Q_4K }, { /* * Kingston HyperX 3k SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "KINGSTON SH103S3*", "*" }, /*quirks*/DA_Q_4K }, { /* * Marvell SSDs (entry taken from OpenSolaris) * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "MARVELL SD88SA02*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Agility 2 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "OCZ-AGILITY2*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Agility 3 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "OCZ-AGILITY3*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Deneva R Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "DENRSTE251M45*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Vertex 2 SSDs (inc pro series) * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "OCZ?VERTEX2*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Vertex 3 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "OCZ-VERTEX3*", "*" }, /*quirks*/DA_Q_4K }, { /* * OCZ Vertex 4 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "OCZ-VERTEX4*", "*" }, /*quirks*/DA_Q_4K }, { /* * Samsung 830 Series SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG SSD 830 Series*", "*" }, /*quirks*/DA_Q_4K }, { /* * Samsung 840 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "Samsung SSD 840*", "*" }, /*quirks*/DA_Q_4K }, { /* * Samsung 843T Series SSDs * 4k optimised */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG MZ7WD*", "*" }, /*quirks*/DA_Q_4K }, { /* * Samsung 850 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "Samsung SSD 850*", "*" }, /*quirks*/DA_Q_4K }, { /* * Samsung PM853T Series SSDs * 4k optimised */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SAMSUNG MZ7GE*", "*" }, /*quirks*/DA_Q_4K }, { /* * SuperTalent TeraDrive CT SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "FTM??CT25H*", "*" }, /*quirks*/DA_Q_4K }, { /* * XceedIOPS SATA SSDs * 4k optimised */ { T_DIRECT, SIP_MEDIA_FIXED, "ATA", "SG9XCS2D*", "*" }, /*quirks*/DA_Q_4K }, }; static disk_strategy_t dastrategy; static dumper_t dadump; static periph_init_t dainit; static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void dasysctlinit(void *context, int pending); static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS); static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS); static void dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method); static off_t dadeletemaxsize(struct da_softc *softc, da_delete_methods delete_method); static void dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method); static void daprobedone(struct cam_periph *periph, union ccb *ccb); static periph_ctor_t daregister; static periph_dtor_t dacleanup; static periph_start_t dastart; static periph_oninv_t daoninvalidate; static void dadone(struct cam_periph *periph, union ccb *done_ccb); static int daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static void daprevent(struct cam_periph *periph, int action); static void dareprobe(struct cam_periph *periph); static void dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector, struct scsi_read_capacity_data_long *rcaplong, size_t rcap_size); static timeout_t dasendorderedtag; static void dashutdown(void *arg, int howto); static timeout_t damediapoll; #ifndef DA_DEFAULT_POLL_PERIOD #define DA_DEFAULT_POLL_PERIOD 3 #endif #ifndef DA_DEFAULT_TIMEOUT #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ #endif #ifndef DA_DEFAULT_RETRY #define DA_DEFAULT_RETRY 4 #endif #ifndef DA_DEFAULT_SEND_ORDERED #define DA_DEFAULT_SEND_ORDERED 1 #endif #define DA_SIO (softc->sort_io_queue >= 0 ? \ softc->sort_io_queue : cam_sort_io_queues) static int da_poll_period = DA_DEFAULT_POLL_PERIOD; static int da_retry_count = DA_DEFAULT_RETRY; static int da_default_timeout = DA_DEFAULT_TIMEOUT; static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, "CAM Direct Access Disk driver"); SYSCTL_INT(_kern_cam_da, OID_AUTO, poll_period, CTLFLAG_RWTUN, &da_poll_period, 0, "Media polling period in seconds"); SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RWTUN, &da_retry_count, 0, "Normal I/O retry count"); SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RWTUN, &da_default_timeout, 0, "Normal I/O timeout (in seconds)"); SYSCTL_INT(_kern_cam_da, OID_AUTO, send_ordered, CTLFLAG_RWTUN, &da_send_ordered, 0, "Send Ordered Tags"); /* * DA_ORDEREDTAG_INTERVAL determines how often, relative * to the default timeout, we check to see whether an ordered * tagged transaction is appropriate to prevent simple tag * starvation. Since we'd like to ensure that there is at least * 1/2 of the timeout length left for a starved transaction to * complete after we've sent an ordered tag, we must poll at least * four times in every timeout period. This takes care of the worst * case where a starved transaction starts during an interval that * meets the requirement "don't send an ordered tag" test so it takes * us two intervals to determine that a tag must be sent. */ #ifndef DA_ORDEREDTAG_INTERVAL #define DA_ORDEREDTAG_INTERVAL 4 #endif static struct periph_driver dadriver = { dainit, "da", TAILQ_HEAD_INITIALIZER(dadriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(da, dadriver); static MALLOC_DEFINE(M_SCSIDA, "scsi_da", "scsi_da buffers"); static int daopen(struct disk *dp) { struct cam_periph *periph; struct da_softc *softc; int error; periph = (struct cam_periph *)dp->d_drv1; if (cam_periph_acquire(periph) != CAM_REQ_CMP) { return (ENXIO); } cam_periph_lock(periph); if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, ("daopen\n")); softc = (struct da_softc *)periph->softc; dareprobe(periph); /* Wait for the disk size update. */ error = cam_periph_sleep(periph, &softc->disk->d_mediasize, PRIBIO, "dareprobe", 0); if (error != 0) xpt_print(periph->path, "unable to retrieve capacity data\n"); if (periph->flags & CAM_PERIPH_INVALID) error = ENXIO; if (error == 0 && (softc->flags & DA_FLAG_PACK_REMOVABLE) != 0 && (softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_PREVENT); if (error == 0) { softc->flags &= ~DA_FLAG_PACK_INVALID; softc->flags |= DA_FLAG_OPEN; } cam_periph_unhold(periph); cam_periph_unlock(periph); if (error != 0) cam_periph_release(periph); return (error); } static int daclose(struct disk *dp) { struct cam_periph *periph; struct da_softc *softc; union ccb *ccb; int error; periph = (struct cam_periph *)dp->d_drv1; softc = (struct da_softc *)periph->softc; cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE | CAM_DEBUG_PERIPH, ("daclose\n")); if (cam_periph_hold(periph, PRIBIO) == 0) { /* Flush disk cache. */ if ((softc->flags & DA_FLAG_DIRTY) != 0 && (softc->quirks & DA_Q_NO_SYNC_CACHE) == 0 && (softc->flags & DA_FLAG_PACK_INVALID) == 0) { ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_synchronize_cache(&ccb->csio, /*retries*/1, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0, /*lb_count*/0, SSD_FULL_SIZE, 5 * 60 * 1000); error = cam_periph_runccb(ccb, daerror, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA | SF_QUIET_IR, softc->disk->d_devstat); if (error == 0) softc->flags &= ~DA_FLAG_DIRTY; xpt_release_ccb(ccb); } /* Allow medium removal. */ if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0 && (softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_ALLOW); cam_periph_unhold(periph); } /* * If we've got removeable media, mark the blocksize as * unavailable, since it could change when new media is * inserted. */ if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0) softc->disk->d_devstat->flags |= DEVSTAT_BS_UNAVAILABLE; softc->flags &= ~DA_FLAG_OPEN; while (softc->refcount != 0) cam_periph_sleep(periph, &softc->refcount, PRIBIO, "daclose", 1); cam_periph_unlock(periph); cam_periph_release(periph); return (0); } static void daschedule(struct cam_periph *periph) { struct da_softc *softc = (struct da_softc *)periph->softc; if (softc->state != DA_STATE_NORMAL) return; /* Check if we have more work to do. */ if (bioq_first(&softc->bio_queue) || (!softc->delete_running && bioq_first(&softc->delete_queue)) || softc->tur) { xpt_schedule(periph, CAM_PRIORITY_NORMAL); } } /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include * only one physical transfer. */ static void dastrategy(struct bio *bp) { struct cam_periph *periph; struct da_softc *softc; periph = (struct cam_periph *)bp->bio_disk->d_drv1; softc = (struct da_softc *)periph->softc; cam_periph_lock(periph); /* * If the device has been made invalid, error out */ if ((softc->flags & DA_FLAG_PACK_INVALID)) { cam_periph_unlock(periph); biofinish(bp, NULL, ENXIO); return; } CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dastrategy(%p)\n", bp)); /* * Place it in the queue of disk activities for this disk */ if (bp->bio_cmd == BIO_DELETE) { bioq_disksort(&softc->delete_queue, bp); } else if (DA_SIO) { bioq_disksort(&softc->bio_queue, bp); } else { bioq_insert_tail(&softc->bio_queue, bp); } /* * Schedule ourselves for performing the work. */ daschedule(periph); cam_periph_unlock(periph); return; } static int dadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct cam_periph *periph; struct da_softc *softc; u_int secsize; struct ccb_scsiio csio; struct disk *dp; int error = 0; dp = arg; periph = dp->d_drv1; softc = (struct da_softc *)periph->softc; cam_periph_lock(periph); secsize = softc->params.secsize; if ((softc->flags & DA_FLAG_PACK_INVALID) != 0) { cam_periph_unlock(periph); return (ENXIO); } if (length > 0) { xpt_setup_ccb(&csio.ccb_h, periph->path, CAM_PRIORITY_NORMAL); csio.ccb_h.ccb_state = DA_CCB_DUMP; scsi_read_write(&csio, /*retries*/0, dadone, MSG_ORDERED_Q_TAG, /*read*/SCSI_RW_WRITE, /*byte2*/0, /*minimum_cmd_size*/ softc->minimum_cmd_size, offset / secsize, length / secsize, /*data_ptr*/(u_int8_t *) virtual, /*dxfer_len*/length, /*sense_len*/SSD_FULL_SIZE, da_default_timeout * 1000); xpt_polled_action((union ccb *)&csio); error = cam_periph_error((union ccb *)&csio, 0, SF_NO_RECOVERY | SF_NO_RETRY, NULL); if ((csio.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(csio.ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); if (error != 0) printf("Aborting dump due to I/O error.\n"); cam_periph_unlock(periph); return (error); } /* * Sync the disk cache contents to the physical media. */ if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) { xpt_setup_ccb(&csio.ccb_h, periph->path, CAM_PRIORITY_NORMAL); csio.ccb_h.ccb_state = DA_CCB_DUMP; scsi_synchronize_cache(&csio, /*retries*/0, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0,/* Cover the whole disk */ /*lb_count*/0, SSD_FULL_SIZE, 5 * 60 * 1000); xpt_polled_action((union ccb *)&csio); error = cam_periph_error((union ccb *)&csio, 0, SF_NO_RECOVERY | SF_NO_RETRY | SF_QUIET_IR, NULL); if ((csio.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(csio.ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); if (error != 0) xpt_print(periph->path, "Synchronize cache failed\n"); } cam_periph_unlock(periph); return (error); } static int dagetattr(struct bio *bp) { int ret; struct cam_periph *periph; periph = (struct cam_periph *)bp->bio_disk->d_drv1; cam_periph_lock(periph); ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, periph->path); cam_periph_unlock(periph); if (ret == 0) bp->bio_completed = bp->bio_length; return ret; } static void dainit(void) { cam_status status; /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, daasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("da: Failed to attach master async callback " "due to status 0x%x!\n", status); } else if (da_send_ordered) { /* Register our shutdown event handler */ if ((EVENTHANDLER_REGISTER(shutdown_post_sync, dashutdown, NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) printf("dainit: shutdown event registration failed!\n"); } } /* * Callback from GEOM, called when it has finished cleaning up its * resources. */ static void dadiskgonecb(struct disk *dp) { struct cam_periph *periph; periph = (struct cam_periph *)dp->d_drv1; cam_periph_release(periph); } static void daoninvalidate(struct cam_periph *periph) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_register_async(0, daasync, periph, periph->path); softc->flags |= DA_FLAG_PACK_INVALID; /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ bioq_flush(&softc->bio_queue, NULL, ENXIO); bioq_flush(&softc->delete_queue, NULL, ENXIO); /* * Tell GEOM that we've gone away, we'll get a callback when it is * done cleaning up its resources. */ disk_gone(softc->disk); } static void dacleanup(struct cam_periph *periph) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; cam_periph_unlock(periph); /* * If we can't free the sysctl tree, oh well... */ if ((softc->flags & DA_FLAG_SCTX_INIT) != 0 && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { xpt_print(periph->path, "can't remove sysctl context\n"); } callout_drain(&softc->mediapoll_c); disk_destroy(softc->disk); callout_drain(&softc->sendordered_c); free(softc, M_DEVBUF); cam_periph_lock(periph); } static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; struct da_softc *softc; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; if (cgd->protocol != PROTO_SCSI) break; if (SID_TYPE(&cgd->inq_data) != T_DIRECT && SID_TYPE(&cgd->inq_data) != T_RBC && SID_TYPE(&cgd->inq_data) != T_OPTICAL) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ status = cam_periph_alloc(daregister, daoninvalidate, dacleanup, dastart, "da", CAM_PERIPH_BIO, path, daasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("daasync: Unable to attach to new device " "due to status 0x%x\n", status); return; } case AC_ADVINFO_CHANGED: { uintptr_t buftype; buftype = (uintptr_t)arg; if (buftype == CDAI_TYPE_PHYS_PATH) { struct da_softc *softc; softc = periph->softc; disk_attr_changed(softc->disk, "GEOM::physpath", M_NOWAIT); } break; } case AC_UNIT_ATTENTION: { union ccb *ccb; int error_code, sense_key, asc, ascq; softc = (struct da_softc *)periph->softc; ccb = (union ccb *)arg; /* * Handle all UNIT ATTENTIONs except our own, * as they will be handled by daerror(). */ if (xpt_path_periph(ccb->ccb_h.path) != periph && scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq)) { if (asc == 0x2A && ascq == 0x09) { xpt_print(ccb->ccb_h.path, "Capacity data has changed\n"); softc->flags &= ~DA_FLAG_PROBED; dareprobe(periph); } else if (asc == 0x28 && ascq == 0x00) { softc->flags &= ~DA_FLAG_PROBED; disk_media_changed(softc->disk, M_NOWAIT); } else if (asc == 0x3F && ascq == 0x03) { xpt_print(ccb->ccb_h.path, "INQUIRY data has changed\n"); softc->flags &= ~DA_FLAG_PROBED; dareprobe(periph); } } cam_periph_async(periph, code, path, arg); break; } case AC_SCSI_AEN: softc = (struct da_softc *)periph->softc; if (!softc->tur) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { softc->tur = 1; daschedule(periph); } } /* FALLTHROUGH */ case AC_SENT_BDR: case AC_BUS_RESET: { struct ccb_hdr *ccbh; softc = (struct da_softc *)periph->softc; /* * Don't fail on the expected unit attention * that will occur. */ softc->flags |= DA_FLAG_RETRY_UA; LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) ccbh->ccb_state |= DA_CCB_RETRY_UA; break; } default: break; } cam_periph_async(periph, code, path, arg); } static void dasysctlinit(void *context, int pending) { struct cam_periph *periph; struct da_softc *softc; char tmpstr[80], tmpstr2[80]; struct ccb_trans_settings cts; periph = (struct cam_periph *)context; /* * periph was held for us when this task was enqueued */ if (periph->flags & CAM_PERIPH_INVALID) { cam_periph_release(periph); return; } softc = (struct da_softc *)periph->softc; snprintf(tmpstr, sizeof(tmpstr), "CAM DA unit %d", periph->unit_number); snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); sysctl_ctx_init(&softc->sysctl_ctx); softc->flags |= DA_FLAG_SCTX_INIT; softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_cam_da), OID_AUTO, tmpstr2, CTLFLAG_RD, 0, tmpstr); if (softc->sysctl_tree == NULL) { printf("dasysctlinit: unable to allocate sysctl tree\n"); cam_periph_release(periph); return; } /* * Now register the sysctl handler, so the user can change the value on * the fly. */ SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW, softc, 0, dadeletemethodsysctl, "A", "BIO_DELETE execution method"); SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW, softc, 0, dadeletemaxsysctl, "Q", "Maximum BIO_DELETE size"); SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", "Minimum CDB size"); SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "sort_io_queue", CTLFLAG_RW, &softc->sort_io_queue, 0, "Sort IO queue to try and optimise disk access patterns"); SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "error_inject", CTLFLAG_RW, &softc->error_inject, 0, "error_inject leaf"); /* * Add some addressing info. */ memset(&cts, 0, sizeof (cts)); xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cam_periph_lock(periph); xpt_action((union ccb *)&cts); cam_periph_unlock(periph); if (cts.ccb_h.status != CAM_REQ_CMP) { cam_periph_release(periph); return; } if (cts.protocol == PROTO_SCSI && cts.transport == XPORT_FC) { struct ccb_trans_settings_fc *fc = &cts.xport_specific.fc; if (fc->valid & CTS_FC_VALID_WWPN) { softc->wwpn = fc->wwpn; SYSCTL_ADD_UQUAD(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "wwpn", CTLFLAG_RD, &softc->wwpn, "World Wide Port Name"); } } cam_periph_release(periph); } static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS) { int error; uint64_t value; struct da_softc *softc; softc = (struct da_softc *)arg1; value = softc->disk->d_delmaxsize; error = sysctl_handle_64(oidp, &value, 0, req); if ((error != 0) || (req->newptr == NULL)) return (error); /* only accept values smaller than the calculated value */ if (value > dadeletemaxsize(softc, softc->delete_method)) { return (EINVAL); } softc->disk->d_delmaxsize = value; return (0); } static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS) { int error, value; value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if ((error != 0) || (req->newptr == NULL)) return (error); /* * Acceptable values here are 6, 10, 12 or 16. */ if (value < 6) value = 6; else if ((value > 6) && (value <= 10)) value = 10; else if ((value > 10) && (value <= 12)) value = 12; else if (value > 12) value = 16; *(int *)arg1 = value; return (0); } static void dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) { softc->delete_method = delete_method; softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method); softc->delete_func = da_delete_functions[delete_method]; if (softc->delete_method > DA_DELETE_DISABLE) softc->disk->d_flags |= DISKFLAG_CANDELETE; else softc->disk->d_flags &= ~DISKFLAG_CANDELETE; } static off_t dadeletemaxsize(struct da_softc *softc, da_delete_methods delete_method) { off_t sectors; switch(delete_method) { case DA_DELETE_UNMAP: sectors = (off_t)softc->unmap_max_lba; break; case DA_DELETE_ATA_TRIM: sectors = (off_t)ATA_DSM_RANGE_MAX * softc->trim_max_ranges; break; case DA_DELETE_WS16: sectors = omin(softc->ws_max_blks, WS16_MAX_BLKS); break; case DA_DELETE_ZERO: case DA_DELETE_WS10: sectors = omin(softc->ws_max_blks, WS10_MAX_BLKS); break; default: return 0; } return (off_t)softc->params.secsize * omin(sectors, softc->params.sectors); } static void daprobedone(struct cam_periph *periph, union ccb *ccb) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; dadeletemethodchoose(softc, DA_DELETE_NONE); if (bootverbose && (softc->flags & DA_FLAG_ANNOUNCED) == 0) { char buf[80]; int i, sep; snprintf(buf, sizeof(buf), "Delete methods: <"); sep = 0; for (i = DA_DELETE_MIN; i <= DA_DELETE_MAX; i++) { if (softc->delete_available & (1 << i)) { if (sep) { strlcat(buf, ",", sizeof(buf)); } else { sep = 1; } strlcat(buf, da_delete_method_names[i], sizeof(buf)); if (i == softc->delete_method) { strlcat(buf, "(*)", sizeof(buf)); } } } if (sep == 0) { if (softc->delete_method == DA_DELETE_NONE) strlcat(buf, "NONE(*)", sizeof(buf)); else strlcat(buf, "DISABLED(*)", sizeof(buf)); } strlcat(buf, ">", sizeof(buf)); printf("%s%d: %s\n", periph->periph_name, periph->unit_number, buf); } /* * Since our peripheral may be invalidated by an error * above or an external event, we must release our CCB * before releasing the probe lock on the peripheral. * The peripheral will only go away once the last lock * is removed, and we need it around for the CCB release * operation. */ xpt_release_ccb(ccb); softc->state = DA_STATE_NORMAL; softc->flags |= DA_FLAG_PROBED; daschedule(periph); wakeup(&softc->disk->d_mediasize); if ((softc->flags & DA_FLAG_ANNOUNCED) == 0) { softc->flags |= DA_FLAG_ANNOUNCED; cam_periph_unhold(periph); } else cam_periph_release_locked(periph); } static void dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method) { int i, delete_method; delete_method = default_method; /* * Use the pre-defined order to choose the best * performing delete. */ for (i = DA_DELETE_MIN; i <= DA_DELETE_MAX; i++) { if (softc->delete_available & (1 << i)) { dadeletemethodset(softc, i); return; } } dadeletemethodset(softc, delete_method); } static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS) { char buf[16]; const char *p; struct da_softc *softc; int i, error, methods, value; softc = (struct da_softc *)arg1; value = softc->delete_method; if (value < 0 || value > DA_DELETE_MAX) p = "UNKNOWN"; else p = da_delete_method_names[value]; strncpy(buf, p, sizeof(buf)); error = sysctl_handle_string(oidp, buf, sizeof(buf), req); if (error != 0 || req->newptr == NULL) return (error); methods = softc->delete_available | (1 << DA_DELETE_DISABLE); for (i = 0; i <= DA_DELETE_MAX; i++) { if (!(methods & (1 << i)) || strcmp(buf, da_delete_method_names[i]) != 0) continue; dadeletemethodset(softc, i); return (0); } return (EINVAL); } static cam_status daregister(struct cam_periph *periph, void *arg) { struct da_softc *softc; struct ccb_pathinq cpi; struct ccb_getdev *cgd; char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) { printf("daregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct da_softc *)malloc(sizeof(*softc), M_DEVBUF, M_NOWAIT|M_ZERO); if (softc == NULL) { printf("daregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } LIST_INIT(&softc->pending_ccbs); softc->state = DA_STATE_PROBE_RC; bioq_init(&softc->bio_queue); bioq_init(&softc->delete_queue); bioq_init(&softc->delete_run_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= DA_FLAG_PACK_REMOVABLE; softc->unmap_max_ranges = UNMAP_MAX_RANGES; softc->unmap_max_lba = UNMAP_RANGE_MAX; softc->ws_max_blks = WS16_MAX_BLKS; softc->trim_max_ranges = ATA_TRIM_MAX_RANGES; softc->sort_io_queue = -1; periph->softc = softc; /* * See if this device has any quirks. */ match = cam_quirkmatch((caddr_t)&cgd->inq_data, (caddr_t)da_quirk_table, sizeof(da_quirk_table)/sizeof(*da_quirk_table), sizeof(*da_quirk_table), scsi_inquiry_match); if (match != NULL) softc->quirks = ((struct da_quirk_entry *)match)->quirks; else softc->quirks = DA_Q_NONE; /* Check if the SIM does not want 6 byte commands */ bzero(&cpi, sizeof(cpi)); xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= DA_Q_NO_6_BYTE; TASK_INIT(&softc->sysctl_task, 0, dasysctlinit, periph); /* * Take an exclusive refcount on the periph while dastart is called * to finish the probe. The reference will be dropped in dadone at * the end of probe. */ (void)cam_periph_hold(periph, PRIBIO); /* * Schedule a periodic event to occasionally send an * ordered tag to a device. */ callout_init_mtx(&softc->sendordered_c, cam_periph_mtx(periph), 0); callout_reset(&softc->sendordered_c, (da_default_timeout * hz) / DA_ORDEREDTAG_INTERVAL, dasendorderedtag, softc); cam_periph_unlock(periph); /* * RBC devices don't have to support READ(6), only READ(10). */ if (softc->quirks & DA_Q_NO_6_BYTE || SID_TYPE(&cgd->inq_data) == T_RBC) softc->minimum_cmd_size = 10; else softc->minimum_cmd_size = 6; /* * Load the user's default, if any. */ snprintf(tmpstr, sizeof(tmpstr), "kern.cam.da.%d.minimum_cmd_size", periph->unit_number); TUNABLE_INT_FETCH(tmpstr, &softc->minimum_cmd_size); /* * 6, 10, 12 and 16 are the currently permissible values. */ if (softc->minimum_cmd_size < 6) softc->minimum_cmd_size = 6; else if ((softc->minimum_cmd_size > 6) && (softc->minimum_cmd_size <= 10)) softc->minimum_cmd_size = 10; else if ((softc->minimum_cmd_size > 10) && (softc->minimum_cmd_size <= 12)) softc->minimum_cmd_size = 12; else if (softc->minimum_cmd_size > 12) softc->minimum_cmd_size = 16; /* Predict whether device may support READ CAPACITY(16). */ if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 && (softc->quirks & DA_Q_NO_RC16) == 0) { softc->flags |= DA_FLAG_CAN_RC16; softc->state = DA_STATE_PROBE_RC16; } /* * Register this media as a disk. */ softc->disk = disk_alloc(); softc->disk->d_devstat = devstat_new_entry(periph->periph_name, periph->unit_number, 0, DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) | XPORT_DEVSTAT_TYPE(cpi.transport), DEVSTAT_PRIORITY_DISK); softc->disk->d_open = daopen; softc->disk->d_close = daclose; softc->disk->d_strategy = dastrategy; softc->disk->d_dump = dadump; softc->disk->d_getattr = dagetattr; softc->disk->d_gone = dadiskgonecb; softc->disk->d_name = "da"; softc->disk->d_drv1 = periph; if (cpi.maxio == 0) softc->maxio = DFLTPHYS; /* traditional default */ else if (cpi.maxio > MAXPHYS) softc->maxio = MAXPHYS; /* for safety */ else softc->maxio = cpi.maxio; softc->disk->d_maxsize = softc->maxio; softc->disk->d_unit = periph->unit_number; softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION; if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; if ((cpi.hba_misc & PIM_UNMAPPED) != 0) softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); cam_strvis(&softc->disk->d_descr[strlen(softc->disk->d_descr)], cgd->inq_data.product, sizeof(cgd->inq_data.product), sizeof(softc->disk->d_descr) - strlen(softc->disk->d_descr)); softc->disk->d_hba_vendor = cpi.hba_vendor; softc->disk->d_hba_device = cpi.hba_device; softc->disk->d_hba_subvendor = cpi.hba_subvendor; softc->disk->d_hba_subdevice = cpi.hba_subdevice; /* * Acquire a reference to the periph before we register with GEOM. * We'll release this reference once GEOM calls us back (via * dadiskgonecb()) telling us that our provider has been freed. */ if (cam_periph_acquire(periph) != CAM_REQ_CMP) { xpt_print(periph->path, "%s: lost periph during " "registration!\n", __func__); cam_periph_lock(periph); return (CAM_REQ_CMP_ERR); } disk_create(softc->disk, DISK_VERSION); cam_periph_lock(periph); /* * Add async callbacks for events of interest. * I don't bother checking if this fails as, * in most cases, the system will function just * fine without them and the only alternative * would be to not attach the device on failure. */ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | AC_ADVINFO_CHANGED | AC_SCSI_AEN | AC_UNIT_ATTENTION, daasync, periph, periph->path); /* * Emit an attribute changed notification just in case * physical path information arrived before our async * event handler was registered, but after anyone attaching * to our disk device polled it. */ disk_attr_changed(softc->disk, "GEOM::physpath", M_NOWAIT); /* * Schedule a periodic media polling events. */ callout_init_mtx(&softc->mediapoll_c, cam_periph_mtx(periph), 0); if ((softc->flags & DA_FLAG_PACK_REMOVABLE) && (cgd->inq_flags & SID_AEN) == 0 && da_poll_period != 0) callout_reset(&softc->mediapoll_c, da_poll_period * hz, damediapoll, periph); xpt_schedule(periph, CAM_PRIORITY_DEV); return(CAM_REQ_CMP); } static void dastart(struct cam_periph *periph, union ccb *start_ccb) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dastart\n")); skipstate: switch (softc->state) { case DA_STATE_NORMAL: { struct bio *bp; uint8_t tag_code; /* Run BIO_DELETE if not running yet. */ if (!softc->delete_running && (bp = bioq_first(&softc->delete_queue)) != NULL) { if (softc->delete_func != NULL) { softc->delete_func(periph, start_ccb, bp); goto out; } else { bioq_flush(&softc->delete_queue, NULL, 0); /* FALLTHROUGH */ } } /* Run regular command. */ bp = bioq_takefirst(&softc->bio_queue); if (bp == NULL) { if (softc->tur) { softc->tur = 0; scsi_test_unit_ready(&start_ccb->csio, /*retries*/ da_retry_count, dadone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_TUR; xpt_action(start_ccb); } else xpt_release_ccb(start_ccb); break; } if (softc->tur) { softc->tur = 0; cam_periph_release_locked(periph); } if ((bp->bio_flags & BIO_ORDERED) != 0 || (softc->flags & DA_FLAG_NEED_OTAG) != 0) { softc->flags &= ~DA_FLAG_NEED_OTAG; softc->flags |= DA_FLAG_WAS_OTAG; tag_code = MSG_ORDERED_Q_TAG; } else { tag_code = MSG_SIMPLE_Q_TAG; } switch (bp->bio_cmd) { case BIO_WRITE: softc->flags |= DA_FLAG_DIRTY; /* FALLTHROUGH */ case BIO_READ: scsi_read_write(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/tag_code, /*read_op*/(bp->bio_cmd == BIO_READ ? SCSI_RW_READ : SCSI_RW_WRITE) | ((bp->bio_flags & BIO_UNMAPPED) != 0 ? SCSI_RW_BIO : 0), /*byte2*/0, softc->minimum_cmd_size, /*lba*/bp->bio_pblkno, /*block_count*/bp->bio_bcount / softc->params.secsize, /*data_ptr*/ (bp->bio_flags & BIO_UNMAPPED) != 0 ? (void *)bp : bp->bio_data, /*dxfer_len*/ bp->bio_bcount, /*sense_len*/SSD_FULL_SIZE, da_default_timeout * 1000); break; case BIO_FLUSH: /* * BIO_FLUSH doesn't currently communicate * range data, so we synchronize the cache * over the whole disk. We also force * ordered tag semantics the flush applies * to all previously queued I/O. */ scsi_synchronize_cache(&start_ccb->csio, /*retries*/1, /*cbfcnp*/dadone, MSG_ORDERED_Q_TAG, /*begin_lba*/0, /*lb_count*/0, SSD_FULL_SIZE, da_default_timeout*1000); break; } start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO; start_ccb->ccb_h.flags |= CAM_UNLOCKED; out: LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, periph_links.le); /* We expect a unit attention from this device */ if ((softc->flags & DA_FLAG_RETRY_UA) != 0) { start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA; softc->flags &= ~DA_FLAG_RETRY_UA; } start_ccb->ccb_h.ccb_bp = bp; softc->refcount++; cam_periph_unlock(periph); xpt_action(start_ccb); cam_periph_lock(periph); softc->refcount--; /* May have more work to do, so ensure we stay scheduled */ daschedule(periph); break; } case DA_STATE_PROBE_RC: { struct scsi_read_capacity_data *rcap; rcap = (struct scsi_read_capacity_data *) malloc(sizeof(*rcap), M_SCSIDA, M_NOWAIT|M_ZERO); if (rcap == NULL) { printf("dastart: Couldn't malloc read_capacity data\n"); /* da_free_periph??? */ break; } scsi_read_capacity(&start_ccb->csio, /*retries*/da_retry_count, dadone, MSG_SIMPLE_Q_TAG, rcap, SSD_FULL_SIZE, /*timeout*/5000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_RC; xpt_action(start_ccb); break; } case DA_STATE_PROBE_RC16: { struct scsi_read_capacity_data_long *rcaplong; rcaplong = (struct scsi_read_capacity_data_long *) malloc(sizeof(*rcaplong), M_SCSIDA, M_NOWAIT|M_ZERO); if (rcaplong == NULL) { printf("dastart: Couldn't malloc read_capacity data\n"); /* da_free_periph??? */ break; } scsi_read_capacity_16(&start_ccb->csio, /*retries*/ da_retry_count, /*cbfcnp*/ dadone, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*lba*/ 0, /*reladr*/ 0, /*pmi*/ 0, /*rcap_buf*/ (uint8_t *)rcaplong, /*rcap_buf_len*/ sizeof(*rcaplong), /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_RC16; xpt_action(start_ccb); break; } case DA_STATE_PROBE_LBP: { struct scsi_vpd_logical_block_prov *lbp; if (!scsi_vpd_supported_page(periph, SVPD_LBP)) { /* * If we get here we don't support any SBC-3 delete * methods with UNMAP as the Logical Block Provisioning * VPD page support is required for devices which * support it according to T10/1799-D Revision 31 * however older revisions of the spec don't mandate * this so we currently don't remove these methods * from the available set. */ softc->state = DA_STATE_PROBE_BLK_LIMITS; goto skipstate; } lbp = (struct scsi_vpd_logical_block_prov *) malloc(sizeof(*lbp), M_SCSIDA, M_NOWAIT|M_ZERO); if (lbp == NULL) { printf("dastart: Couldn't malloc lbp data\n"); /* da_free_periph??? */ break; } scsi_inquiry(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*inq_buf*/(u_int8_t *)lbp, /*inq_len*/sizeof(*lbp), /*evpd*/TRUE, /*page_code*/SVPD_LBP, /*sense_len*/SSD_MIN_SIZE, /*timeout*/da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_LBP; xpt_action(start_ccb); break; } case DA_STATE_PROBE_BLK_LIMITS: { struct scsi_vpd_block_limits *block_limits; if (!scsi_vpd_supported_page(periph, SVPD_BLOCK_LIMITS)) { /* Not supported skip to next probe */ softc->state = DA_STATE_PROBE_BDC; goto skipstate; } block_limits = (struct scsi_vpd_block_limits *) malloc(sizeof(*block_limits), M_SCSIDA, M_NOWAIT|M_ZERO); if (block_limits == NULL) { printf("dastart: Couldn't malloc block_limits data\n"); /* da_free_periph??? */ break; } scsi_inquiry(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*inq_buf*/(u_int8_t *)block_limits, /*inq_len*/sizeof(*block_limits), /*evpd*/TRUE, /*page_code*/SVPD_BLOCK_LIMITS, /*sense_len*/SSD_MIN_SIZE, /*timeout*/da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_BLK_LIMITS; xpt_action(start_ccb); break; } case DA_STATE_PROBE_BDC: { struct scsi_vpd_block_characteristics *bdc; if (!scsi_vpd_supported_page(periph, SVPD_BDC)) { softc->state = DA_STATE_PROBE_ATA; goto skipstate; } bdc = (struct scsi_vpd_block_characteristics *) malloc(sizeof(*bdc), M_SCSIDA, M_NOWAIT|M_ZERO); if (bdc == NULL) { printf("dastart: Couldn't malloc bdc data\n"); /* da_free_periph??? */ break; } scsi_inquiry(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*inq_buf*/(u_int8_t *)bdc, /*inq_len*/sizeof(*bdc), /*evpd*/TRUE, /*page_code*/SVPD_BDC, /*sense_len*/SSD_MIN_SIZE, /*timeout*/da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_BDC; xpt_action(start_ccb); break; } case DA_STATE_PROBE_ATA: { struct ata_params *ata_params; if (!scsi_vpd_supported_page(periph, SVPD_ATA_INFORMATION)) { daprobedone(periph, start_ccb); break; } ata_params = (struct ata_params*) malloc(sizeof(*ata_params), M_SCSIDA, M_NOWAIT|M_ZERO); if (ata_params == NULL) { printf("dastart: Couldn't malloc ata_params data\n"); /* da_free_periph??? */ break; } scsi_ata_identify(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*data_ptr*/(u_int8_t *)ata_params, /*dxfer_len*/sizeof(*ata_params), /*sense_len*/SSD_FULL_SIZE, /*timeout*/da_default_timeout * 1000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE_ATA; xpt_action(start_ccb); break; } } } /* * In each of the methods below, while its the caller's * responsibility to ensure the request will fit into a * single device request, we might have changed the delete * method due to the device incorrectly advertising either * its supported methods or limits. * * To prevent this causing further issues we validate the * against the methods limits, and warn which would * otherwise be unnecessary. */ static void da_delete_unmap(struct cam_periph *periph, union ccb *ccb, struct bio *bp) { struct da_softc *softc = (struct da_softc *)periph->softc;; struct bio *bp1; uint8_t *buf = softc->unmap_buf; uint64_t lba, lastlba = (uint64_t)-1; uint64_t totalcount = 0; uint64_t count; uint32_t lastcount = 0, c; uint32_t off, ranges = 0; /* * Currently this doesn't take the UNMAP * Granularity and Granularity Alignment * fields into account. * * This could result in both unoptimal unmap * requests as as well as UNMAP calls unmapping * fewer LBA's than requested. */ softc->delete_running = 1; bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); bp1 = bp; do { bioq_remove(&softc->delete_queue, bp1); if (bp1 != bp) bioq_insert_tail(&softc->delete_run_queue, bp1); lba = bp1->bio_pblkno; count = bp1->bio_bcount / softc->params.secsize; /* Try to extend the previous range. */ if (lba == lastlba) { c = omin(count, UNMAP_RANGE_MAX - lastcount); lastcount += c; off = ((ranges - 1) * UNMAP_RANGE_SIZE) + UNMAP_HEAD_SIZE; scsi_ulto4b(lastcount, &buf[off + 8]); count -= c; lba +=c; totalcount += c; } while (count > 0) { c = omin(count, UNMAP_RANGE_MAX); if (totalcount + c > softc->unmap_max_lba || ranges >= softc->unmap_max_ranges) { xpt_print(periph->path, "%s issuing short delete %ld > %ld" "|| %d >= %d", da_delete_method_desc[softc->delete_method], totalcount + c, softc->unmap_max_lba, ranges, softc->unmap_max_ranges); break; } off = (ranges * UNMAP_RANGE_SIZE) + UNMAP_HEAD_SIZE; scsi_u64to8b(lba, &buf[off + 0]); scsi_ulto4b(c, &buf[off + 8]); lba += c; totalcount += c; ranges++; count -= c; lastcount = c; } lastlba = lba; bp1 = bioq_first(&softc->delete_queue); if (bp1 == NULL || ranges >= softc->unmap_max_ranges || totalcount + bp1->bio_bcount / softc->params.secsize > softc->unmap_max_lba) break; } while (1); scsi_ulto2b(ranges * 16 + 6, &buf[0]); scsi_ulto2b(ranges * 16, &buf[2]); scsi_unmap(&ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*byte2*/0, /*data_ptr*/ buf, /*dxfer_len*/ ranges * 16 + 8, /*sense_len*/SSD_FULL_SIZE, da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; } static void da_delete_trim(struct cam_periph *periph, union ccb *ccb, struct bio *bp) { struct da_softc *softc = (struct da_softc *)periph->softc; struct bio *bp1; uint8_t *buf = softc->unmap_buf; uint64_t lastlba = (uint64_t)-1; uint64_t count; uint64_t lba; uint32_t lastcount = 0, c, requestcount; int ranges = 0, off, block_count; softc->delete_running = 1; bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); bp1 = bp; do { bioq_remove(&softc->delete_queue, bp1); if (bp1 != bp) bioq_insert_tail(&softc->delete_run_queue, bp1); lba = bp1->bio_pblkno; count = bp1->bio_bcount / softc->params.secsize; requestcount = count; /* Try to extend the previous range. */ if (lba == lastlba) { c = omin(count, ATA_DSM_RANGE_MAX - lastcount); lastcount += c; off = (ranges - 1) * 8; buf[off + 6] = lastcount & 0xff; buf[off + 7] = (lastcount >> 8) & 0xff; count -= c; lba += c; } while (count > 0) { c = omin(count, ATA_DSM_RANGE_MAX); off = ranges * 8; buf[off + 0] = lba & 0xff; buf[off + 1] = (lba >> 8) & 0xff; buf[off + 2] = (lba >> 16) & 0xff; buf[off + 3] = (lba >> 24) & 0xff; buf[off + 4] = (lba >> 32) & 0xff; buf[off + 5] = (lba >> 40) & 0xff; buf[off + 6] = c & 0xff; buf[off + 7] = (c >> 8) & 0xff; lba += c; ranges++; count -= c; lastcount = c; if (count != 0 && ranges == softc->trim_max_ranges) { xpt_print(periph->path, "%s issuing short delete %ld > %ld\n", da_delete_method_desc[softc->delete_method], requestcount, (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX); break; } } lastlba = lba; bp1 = bioq_first(&softc->delete_queue); if (bp1 == NULL || bp1->bio_bcount / softc->params.secsize > (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) break; } while (1); block_count = (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES; scsi_ata_trim(&ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, block_count, /*data_ptr*/buf, /*dxfer_len*/block_count * ATA_DSM_BLK_SIZE, /*sense_len*/SSD_FULL_SIZE, da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; } /* * We calculate ws_max_blks here based off d_delmaxsize instead * of using softc->ws_max_blks as it is absolute max for the * device not the protocol max which may well be lower. */ static void da_delete_ws(struct cam_periph *periph, union ccb *ccb, struct bio *bp) { struct da_softc *softc; struct bio *bp1; uint64_t ws_max_blks; uint64_t lba; uint64_t count; /* forward compat with WS32 */ softc = (struct da_softc *)periph->softc; ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize; softc->delete_running = 1; lba = bp->bio_pblkno; count = 0; bp1 = bp; do { bioq_remove(&softc->delete_queue, bp1); if (bp1 != bp) bioq_insert_tail(&softc->delete_run_queue, bp1); count += bp1->bio_bcount / softc->params.secsize; if (count > ws_max_blks) { xpt_print(periph->path, "%s issuing short delete %ld > %ld\n", da_delete_method_desc[softc->delete_method], count, ws_max_blks); count = omin(count, ws_max_blks); break; } bp1 = bioq_first(&softc->delete_queue); if (bp1 == NULL || lba + count != bp1->bio_pblkno || count + bp1->bio_bcount / softc->params.secsize > ws_max_blks) break; } while (1); scsi_write_same(&ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/MSG_SIMPLE_Q_TAG, /*byte2*/softc->delete_method == DA_DELETE_ZERO ? 0 : SWS_UNMAP, softc->delete_method == DA_DELETE_WS16 ? 16 : 10, /*lba*/lba, /*block_count*/count, /*data_ptr*/ __DECONST(void *, zero_region), /*dxfer_len*/ softc->params.secsize, /*sense_len*/SSD_FULL_SIZE, da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; } static int cmd6workaround(union ccb *ccb) { struct scsi_rw_6 cmd6; struct scsi_rw_10 *cmd10; struct da_softc *softc; u_int8_t *cdb; struct bio *bp; int frozen; cdb = ccb->csio.cdb_io.cdb_bytes; softc = (struct da_softc *)xpt_path_periph(ccb->ccb_h.path)->softc; if (ccb->ccb_h.ccb_state == DA_CCB_DELETE) { da_delete_methods old_method = softc->delete_method; /* * Typically there are two reasons for failure here * 1. Delete method was detected as supported but isn't * 2. Delete failed due to invalid params e.g. too big * * While we will attempt to choose an alternative delete method * this may result in short deletes if the existing delete * requests from geom are big for the new method choosen. * * This method assumes that the error which triggered this * will not retry the io otherwise a panic will occur */ dadeleteflag(softc, old_method, 0); dadeletemethodchoose(softc, DA_DELETE_DISABLE); if (softc->delete_method == DA_DELETE_DISABLE) xpt_print(ccb->ccb_h.path, "%s failed, disabling BIO_DELETE\n", da_delete_method_desc[old_method]); else xpt_print(ccb->ccb_h.path, "%s failed, switching to %s BIO_DELETE\n", da_delete_method_desc[old_method], da_delete_method_desc[softc->delete_method]); while ((bp = bioq_takefirst(&softc->delete_run_queue)) != NULL) bioq_disksort(&softc->delete_queue, bp); bioq_disksort(&softc->delete_queue, (struct bio *)ccb->ccb_h.ccb_bp); ccb->ccb_h.ccb_bp = NULL; return (0); } /* Detect unsupported PREVENT ALLOW MEDIUM REMOVAL. */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) == 0 && (*cdb == PREVENT_ALLOW) && (softc->quirks & DA_Q_NO_PREVENT) == 0) { if (bootverbose) xpt_print(ccb->ccb_h.path, "PREVENT ALLOW MEDIUM REMOVAL not supported.\n"); softc->quirks |= DA_Q_NO_PREVENT; return (0); } /* Detect unsupported SYNCHRONIZE CACHE(10). */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) == 0 && (*cdb == SYNCHRONIZE_CACHE) && (softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) { if (bootverbose) xpt_print(ccb->ccb_h.path, "SYNCHRONIZE CACHE(10) not supported.\n"); softc->quirks |= DA_Q_NO_SYNC_CACHE; softc->disk->d_flags &= ~DISKFLAG_CANFLUSHCACHE; return (0); } /* Translation only possible if CDB is an array and cmd is R/W6 */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0 || (*cdb != READ_6 && *cdb != WRITE_6)) return 0; xpt_print(ccb->ccb_h.path, "READ(6)/WRITE(6) not supported, " "increasing minimum_cmd_size to 10.\n"); softc->minimum_cmd_size = 10; bcopy(cdb, &cmd6, sizeof(struct scsi_rw_6)); cmd10 = (struct scsi_rw_10 *)cdb; cmd10->opcode = (cmd6.opcode == READ_6) ? READ_10 : WRITE_10; cmd10->byte2 = 0; scsi_ulto4b(scsi_3btoul(cmd6.addr), cmd10->addr); cmd10->reserved = 0; scsi_ulto2b(cmd6.length, cmd10->length); cmd10->control = cmd6.control; ccb->csio.cdb_len = sizeof(*cmd10); /* Requeue request, unfreezing queue if necessary */ frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_action(ccb); if (frozen) { cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } return (ERESTART); } static void dadone(struct cam_periph *periph, union ccb *done_ccb) { struct da_softc *softc; struct ccb_scsiio *csio; u_int32_t priority; da_ccb_state state; softc = (struct da_softc *)periph->softc; priority = done_ccb->ccb_h.pinfo.priority; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("dadone\n")); csio = &done_ccb->csio; state = csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK; switch (state) { case DA_CCB_BUFFER_IO: case DA_CCB_DELETE: { struct bio *bp, *bp1; cam_periph_lock(periph); bp = (struct bio *)done_ccb->ccb_h.ccb_bp; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { int error; int sf; if ((csio->ccb_h.ccb_state & DA_CCB_RETRY_UA) != 0) sf = SF_RETRY_UA; else sf = 0; error = daerror(done_ccb, CAM_RETRY_SELTO, sf); if (error == ERESTART) { /* * A retry was scheduled, so * just return. */ cam_periph_unlock(periph); return; } bp = (struct bio *)done_ccb->ccb_h.ccb_bp; if (error != 0) { int queued_error; /* * return all queued I/O with EIO, so that * the client can retry these I/Os in the * proper order should it attempt to recover. */ queued_error = EIO; if (error == ENXIO && (softc->flags & DA_FLAG_PACK_INVALID)== 0) { /* * Catastrophic error. Mark our pack as * invalid. */ /* * XXX See if this is really a media * XXX change first? */ xpt_print(periph->path, "Invalidating pack\n"); softc->flags |= DA_FLAG_PACK_INVALID; queued_error = ENXIO; } bioq_flush(&softc->bio_queue, NULL, queued_error); if (bp != NULL) { bp->bio_error = error; bp->bio_resid = bp->bio_bcount; bp->bio_flags |= BIO_ERROR; } } else if (bp != NULL) { if (state == DA_CCB_DELETE) bp->bio_resid = 0; else bp->bio_resid = csio->resid; bp->bio_error = 0; if (bp->bio_resid != 0) bp->bio_flags |= BIO_ERROR; } if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } else if (bp != NULL) { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) panic("REQ_CMP with QFRZN"); if (state == DA_CCB_DELETE) bp->bio_resid = 0; else bp->bio_resid = csio->resid; if (csio->resid > 0) bp->bio_flags |= BIO_ERROR; if (softc->error_inject != 0) { bp->bio_error = softc->error_inject; bp->bio_resid = bp->bio_bcount; bp->bio_flags |= BIO_ERROR; softc->error_inject = 0; } } LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); if (LIST_EMPTY(&softc->pending_ccbs)) softc->flags |= DA_FLAG_WAS_OTAG; xpt_release_ccb(done_ccb); if (state == DA_CCB_DELETE) { TAILQ_HEAD(, bio) queue; TAILQ_INIT(&queue); TAILQ_CONCAT(&queue, &softc->delete_run_queue.queue, bio_queue); softc->delete_run_queue.insert_point = NULL; /* * Normally, the xpt_release_ccb() above would make sure * that when we have more work to do, that work would * get kicked off. However, we specifically keep * delete_running set to 0 before the call above to * allow other I/O to progress when many BIO_DELETE * requests are pushed down. We set delete_running to 0 * and call daschedule again so that we don't stall if * there are no other I/Os pending apart from BIO_DELETEs. */ softc->delete_running = 0; daschedule(periph); cam_periph_unlock(periph); while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { TAILQ_REMOVE(&queue, bp1, bio_queue); bp1->bio_error = bp->bio_error; if (bp->bio_flags & BIO_ERROR) { bp1->bio_flags |= BIO_ERROR; bp1->bio_resid = bp1->bio_bcount; } else bp1->bio_resid = 0; biodone(bp1); } } else cam_periph_unlock(periph); if (bp != NULL) biodone(bp); return; } case DA_CCB_PROBE_RC: case DA_CCB_PROBE_RC16: { struct scsi_read_capacity_data *rdcap; struct scsi_read_capacity_data_long *rcaplong; char announce_buf[80]; int lbp; lbp = 0; rdcap = NULL; rcaplong = NULL; if (state == DA_CCB_PROBE_RC) rdcap =(struct scsi_read_capacity_data *)csio->data_ptr; else rcaplong = (struct scsi_read_capacity_data_long *) csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct disk_params *dp; uint32_t block_size; uint64_t maxsector; u_int lalba; /* Lowest aligned LBA. */ if (state == DA_CCB_PROBE_RC) { block_size = scsi_4btoul(rdcap->length); maxsector = scsi_4btoul(rdcap->addr); lalba = 0; /* * According to SBC-2, if the standard 10 * byte READ CAPACITY command returns 2^32, * we should issue the 16 byte version of * the command, since the device in question * has more sectors than can be represented * with the short version of the command. */ if (maxsector == 0xffffffff) { free(rdcap, M_SCSIDA); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_RC16; xpt_schedule(periph, priority); return; } } else { block_size = scsi_4btoul(rcaplong->length); maxsector = scsi_8btou64(rcaplong->addr); lalba = scsi_2btoul(rcaplong->lalba_lbp); } /* * Because GEOM code just will panic us if we * give them an 'illegal' value we'll avoid that * here. */ if (block_size == 0) { block_size = 512; if (maxsector == 0) maxsector = -1; } if (block_size >= MAXPHYS) { xpt_print(periph->path, "unsupportable block size %ju\n", (uintmax_t) block_size); announce_buf[0] = '\0'; cam_periph_invalidate(periph); } else { /* * We pass rcaplong into dasetgeom(), * because it will only use it if it is * non-NULL. */ dasetgeom(periph, block_size, maxsector, rcaplong, sizeof(*rcaplong)); lbp = (lalba & SRC16_LBPME_A); dp = &softc->params; snprintf(announce_buf, sizeof(announce_buf), "%juMB (%ju %u byte sectors: %dH %dS/T " "%dC)", (uintmax_t) (((uintmax_t)dp->secsize * dp->sectors) / (1024*1024)), (uintmax_t)dp->sectors, dp->secsize, dp->heads, dp->secs_per_track, dp->cylinders); } } else { int error; announce_buf[0] = '\0'; /* * Retry any UNIT ATTENTION type errors. They * are expected at boot. */ error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } else if (error != 0) { int asc, ascq; int sense_key, error_code; int have_sense; cam_status status; struct ccb_getdev cgd; /* Don't wedge this device's queue */ status = done_ccb->ccb_h.status; if ((status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, CAM_PRIORITY_NORMAL); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if (scsi_extract_sense_ccb(done_ccb, &error_code, &sense_key, &asc, &ascq)) have_sense = TRUE; else have_sense = FALSE; /* * If we tried READ CAPACITY(16) and failed, * fallback to READ CAPACITY(10). */ if ((state == DA_CCB_PROBE_RC16) && (softc->flags & DA_FLAG_CAN_RC16) && (((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) || ((have_sense) && (error_code == SSD_CURRENT_ERROR) && (sense_key == SSD_KEY_ILLEGAL_REQUEST)))) { softc->flags &= ~DA_FLAG_CAN_RC16; free(rdcap, M_SCSIDA); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_RC; xpt_schedule(periph, priority); return; } else /* * Attach to anything that claims to be a * direct access or optical disk device, * as long as it doesn't return a "Logical * unit not supported" (0x25) error. */ if ((have_sense) && (asc != 0x25) && (error_code == SSD_CURRENT_ERROR)) { const char *sense_key_desc; const char *asc_desc; dasetgeom(periph, 512, -1, NULL, 0); scsi_sense_desc(sense_key, asc, ascq, &cgd.inq_data, &sense_key_desc, &asc_desc); snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: %s, %s", sense_key_desc, asc_desc); } else { if (have_sense) scsi_sense_print( &done_ccb->csio); else { xpt_print(periph->path, "got CAM status %#x\n", done_ccb->ccb_h.status); } xpt_print(periph->path, "fatal error, " "failed to attach to device\n"); /* * Free up resources. */ cam_periph_invalidate(periph); } } } free(csio->data_ptr, M_SCSIDA); if (announce_buf[0] != '\0' && ((softc->flags & DA_FLAG_ANNOUNCED) == 0)) { /* * Create our sysctl variables, now that we know * we have successfully attached. */ /* increase the refcount */ if (cam_periph_acquire(periph) == CAM_REQ_CMP) { taskqueue_enqueue(taskqueue_thread, &softc->sysctl_task); xpt_announce_periph(periph, announce_buf); xpt_announce_quirks(periph, softc->quirks, DA_Q_BIT_STRING); } else { xpt_print(periph->path, "fatal error, " "could not acquire reference count\n"); } } /* We already probed the device. */ if (softc->flags & DA_FLAG_PROBED) { daprobedone(periph, done_ccb); return; } /* Ensure re-probe doesn't see old delete. */ softc->delete_available = 0; if (lbp && (softc->quirks & DA_Q_NO_UNMAP) == 0) { /* * Based on older SBC-3 spec revisions * any of the UNMAP methods "may" be * available via LBP given this flag so * we flag all of them as availble and * then remove those which further * probes confirm aren't available * later. * * We could also check readcap(16) p_type * flag to exclude one or more invalid * write same (X) types here */ dadeleteflag(softc, DA_DELETE_WS16, 1); dadeleteflag(softc, DA_DELETE_WS10, 1); dadeleteflag(softc, DA_DELETE_ZERO, 1); dadeleteflag(softc, DA_DELETE_UNMAP, 1); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_LBP; xpt_schedule(periph, priority); return; } xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_BDC; xpt_schedule(periph, priority); return; } case DA_CCB_PROBE_LBP: { struct scsi_vpd_logical_block_prov *lbp; lbp = (struct scsi_vpd_logical_block_prov *)csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { /* * T10/1799-D Revision 31 states at least one of these * must be supported but we don't currently enforce this. */ dadeleteflag(softc, DA_DELETE_WS16, (lbp->flags & SVPD_LBP_WS16)); dadeleteflag(softc, DA_DELETE_WS10, (lbp->flags & SVPD_LBP_WS10)); dadeleteflag(softc, DA_DELETE_ZERO, (lbp->flags & SVPD_LBP_WS10)); dadeleteflag(softc, DA_DELETE_UNMAP, (lbp->flags & SVPD_LBP_UNMAP)); } else { int error; error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) return; else if (error != 0) { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge this device's queue */ cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } /* * Failure indicates we don't support any SBC-3 * delete methods with UNMAP */ } } free(lbp, M_SCSIDA); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_BLK_LIMITS; xpt_schedule(periph, priority); return; } case DA_CCB_PROBE_BLK_LIMITS: { struct scsi_vpd_block_limits *block_limits; block_limits = (struct scsi_vpd_block_limits *)csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { uint32_t max_txfer_len = scsi_4btoul( block_limits->max_txfer_len); uint32_t max_unmap_lba_cnt = scsi_4btoul( block_limits->max_unmap_lba_cnt); uint32_t max_unmap_blk_cnt = scsi_4btoul( block_limits->max_unmap_blk_cnt); uint64_t ws_max_blks = scsi_8btou64( block_limits->max_write_same_length); if (max_txfer_len != 0) { softc->disk->d_maxsize = MIN(softc->maxio, (off_t)max_txfer_len * softc->params.secsize); } /* * We should already support UNMAP but we check lba * and block count to be sure */ if (max_unmap_lba_cnt != 0x00L && max_unmap_blk_cnt != 0x00L) { softc->unmap_max_lba = max_unmap_lba_cnt; softc->unmap_max_ranges = min(max_unmap_blk_cnt, UNMAP_MAX_RANGES); } else { /* * Unexpected UNMAP limits which means the * device doesn't actually support UNMAP */ dadeleteflag(softc, DA_DELETE_UNMAP, 0); } if (ws_max_blks != 0x00L) softc->ws_max_blks = ws_max_blks; } else { int error; error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) return; else if (error != 0) { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge this device's queue */ cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } /* * Failure here doesn't mean UNMAP is not * supported as this is an optional page. */ softc->unmap_max_lba = 1; softc->unmap_max_ranges = 1; } } free(block_limits, M_SCSIDA); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_BDC; xpt_schedule(periph, priority); return; } case DA_CCB_PROBE_BDC: { struct scsi_vpd_block_characteristics *bdc; bdc = (struct scsi_vpd_block_characteristics *)csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { /* * Disable queue sorting for non-rotational media * by default. */ u_int16_t old_rate = softc->disk->d_rotation_rate; softc->disk->d_rotation_rate = scsi_2btoul(bdc->medium_rotation_rate); if (softc->disk->d_rotation_rate == SVPD_BDC_RATE_NON_ROTATING) { softc->sort_io_queue = 0; } if (softc->disk->d_rotation_rate != old_rate) { disk_attr_changed(softc->disk, "GEOM::rotation_rate", M_NOWAIT); } } else { int error; error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) return; else if (error != 0) { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge this device's queue */ cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } } } free(bdc, M_SCSIDA); xpt_release_ccb(done_ccb); softc->state = DA_STATE_PROBE_ATA; xpt_schedule(periph, priority); return; } case DA_CCB_PROBE_ATA: { int i; struct ata_params *ata_params; int16_t *ptr; ata_params = (struct ata_params *)csio->data_ptr; ptr = (uint16_t *)ata_params; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { uint16_t old_rate; for (i = 0; i < sizeof(*ata_params) / 2; i++) ptr[i] = le16toh(ptr[i]); if (ata_params->support_dsm & ATA_SUPPORT_DSM_TRIM && (softc->quirks & DA_Q_NO_UNMAP) == 0) { dadeleteflag(softc, DA_DELETE_ATA_TRIM, 1); if (ata_params->max_dsm_blocks != 0) softc->trim_max_ranges = min( softc->trim_max_ranges, ata_params->max_dsm_blocks * ATA_DSM_BLK_RANGES); } /* * Disable queue sorting for non-rotational media * by default. */ old_rate = softc->disk->d_rotation_rate; softc->disk->d_rotation_rate = ata_params->media_rotation_rate; if (softc->disk->d_rotation_rate == ATA_RATE_NON_ROTATING) { softc->sort_io_queue = 0; } if (softc->disk->d_rotation_rate != old_rate) { disk_attr_changed(softc->disk, "GEOM::rotation_rate", M_NOWAIT); } } else { int error; error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) return; else if (error != 0) { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge this device's queue */ cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } } } free(ata_params, M_SCSIDA); daprobedone(periph, done_ccb); return; } case DA_CCB_DUMP: /* No-op. We're polling */ return; case DA_CCB_TUR: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) == ERESTART) return; if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } xpt_release_ccb(done_ccb); cam_periph_release_locked(periph); return; } default: break; } xpt_release_ccb(done_ccb); } static void dareprobe(struct cam_periph *periph) { struct da_softc *softc; cam_status status; softc = (struct da_softc *)periph->softc; /* Probe in progress; don't interfere. */ if (softc->state != DA_STATE_NORMAL) return; status = cam_periph_acquire(periph); KASSERT(status == CAM_REQ_CMP, ("dareprobe: cam_periph_acquire failed")); if (softc->flags & DA_FLAG_CAN_RC16) softc->state = DA_STATE_PROBE_RC16; else softc->state = DA_STATE_PROBE_RC; xpt_schedule(periph, CAM_PRIORITY_DEV); } static int daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct da_softc *softc; struct cam_periph *periph; int error, error_code, sense_key, asc, ascq; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct da_softc *)periph->softc; /* * Automatically detect devices that do not support * READ(6)/WRITE(6) and upgrade to using 10 byte cdbs. */ error = 0; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { error = cmd6workaround(ccb); } else if (scsi_extract_sense_ccb(ccb, &error_code, &sense_key, &asc, &ascq)) { if (sense_key == SSD_KEY_ILLEGAL_REQUEST) error = cmd6workaround(ccb); /* * If the target replied with CAPACITY DATA HAS CHANGED UA, * query the capacity and notify upper layers. */ else if (sense_key == SSD_KEY_UNIT_ATTENTION && asc == 0x2A && ascq == 0x09) { xpt_print(periph->path, "Capacity data has changed\n"); softc->flags &= ~DA_FLAG_PROBED; dareprobe(periph); sense_flags |= SF_NO_PRINT; } else if (sense_key == SSD_KEY_UNIT_ATTENTION && asc == 0x28 && ascq == 0x00) { softc->flags &= ~DA_FLAG_PROBED; disk_media_changed(softc->disk, M_NOWAIT); } else if (sense_key == SSD_KEY_UNIT_ATTENTION && asc == 0x3F && ascq == 0x03) { xpt_print(periph->path, "INQUIRY data has changed\n"); softc->flags &= ~DA_FLAG_PROBED; dareprobe(periph); sense_flags |= SF_NO_PRINT; } else if (sense_key == SSD_KEY_NOT_READY && asc == 0x3a && (softc->flags & DA_FLAG_PACK_INVALID) == 0) { softc->flags |= DA_FLAG_PACK_INVALID; disk_media_gone(softc->disk, M_NOWAIT); } } if (error == ERESTART) return (ERESTART); /* * XXX * Until we have a better way of doing pack validation, * don't treat UAs as errors. */ sense_flags |= SF_RETRY_UA; + + if (softc->quirks & DA_Q_RETRY_BUSY) + sense_flags |= SF_RETRY_BUSY; return(cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } static void damediapoll(void *arg) { struct cam_periph *periph = arg; struct da_softc *softc = periph->softc; if (!softc->tur && LIST_EMPTY(&softc->pending_ccbs)) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { softc->tur = 1; daschedule(periph); } } /* Queue us up again */ if (da_poll_period != 0) callout_schedule(&softc->mediapoll_c, da_poll_period * hz); } static void daprevent(struct cam_periph *periph, int action) { struct da_softc *softc; union ccb *ccb; int error; softc = (struct da_softc *)periph->softc; if (((action == PR_ALLOW) && (softc->flags & DA_FLAG_PACK_LOCKED) == 0) || ((action == PR_PREVENT) && (softc->flags & DA_FLAG_PACK_LOCKED) != 0)) { return; } ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_prevent(&ccb->csio, /*retries*/1, /*cbcfp*/dadone, MSG_SIMPLE_Q_TAG, action, SSD_FULL_SIZE, 5000); error = cam_periph_runccb(ccb, daerror, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_PRINT, softc->disk->d_devstat); if (error == 0) { if (action == PR_ALLOW) softc->flags &= ~DA_FLAG_PACK_LOCKED; else softc->flags |= DA_FLAG_PACK_LOCKED; } xpt_release_ccb(ccb); } static void dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector, struct scsi_read_capacity_data_long *rcaplong, size_t rcap_len) { struct ccb_calc_geometry ccg; struct da_softc *softc; struct disk_params *dp; u_int lbppbe, lalba; int error; softc = (struct da_softc *)periph->softc; dp = &softc->params; dp->secsize = block_len; dp->sectors = maxsector + 1; if (rcaplong != NULL) { lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE; lalba = scsi_2btoul(rcaplong->lalba_lbp); lalba &= SRC16_LALBA_A; } else { lbppbe = 0; lalba = 0; } if (lbppbe > 0) { dp->stripesize = block_len << lbppbe; dp->stripeoffset = (dp->stripesize - block_len * lalba) % dp->stripesize; } else if (softc->quirks & DA_Q_4K) { dp->stripesize = 4096; dp->stripeoffset = 0; } else { dp->stripesize = 0; dp->stripeoffset = 0; } /* * Have the controller provide us with a geometry * for this disk. The only time the geometry * matters is when we boot and the controller * is the only one knowledgeable enough to come * up with something that will make this a bootable * device. */ xpt_setup_ccb(&ccg.ccb_h, periph->path, CAM_PRIORITY_NORMAL); ccg.ccb_h.func_code = XPT_CALC_GEOMETRY; ccg.block_size = dp->secsize; ccg.volume_size = dp->sectors; ccg.heads = 0; ccg.secs_per_track = 0; ccg.cylinders = 0; xpt_action((union ccb*)&ccg); if ((ccg.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { /* * We don't know what went wrong here- but just pick * a geometry so we don't have nasty things like divide * by zero. */ dp->heads = 255; dp->secs_per_track = 255; dp->cylinders = dp->sectors / (255 * 255); if (dp->cylinders == 0) { dp->cylinders = 1; } } else { dp->heads = ccg.heads; dp->secs_per_track = ccg.secs_per_track; dp->cylinders = ccg.cylinders; } /* * If the user supplied a read capacity buffer, and if it is * different than the previous buffer, update the data in the EDT. * If it's the same, we don't bother. This avoids sending an * update every time someone opens this device. */ if ((rcaplong != NULL) && (bcmp(rcaplong, &softc->rcaplong, min(sizeof(softc->rcaplong), rcap_len)) != 0)) { struct ccb_dev_advinfo cdai; xpt_setup_ccb(&cdai.ccb_h, periph->path, CAM_PRIORITY_NORMAL); cdai.ccb_h.func_code = XPT_DEV_ADVINFO; cdai.buftype = CDAI_TYPE_RCAPLONG; cdai.flags |= CDAI_FLAG_STORE; cdai.bufsiz = rcap_len; cdai.buf = (uint8_t *)rcaplong; xpt_action((union ccb *)&cdai); if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); if (cdai.ccb_h.status != CAM_REQ_CMP) { xpt_print(periph->path, "%s: failed to set read " "capacity advinfo\n", __func__); /* Use cam_error_print() to decode the status */ cam_error_print((union ccb *)&cdai, CAM_ESF_CAM_STATUS, CAM_EPF_ALL); } else { bcopy(rcaplong, &softc->rcaplong, min(sizeof(softc->rcaplong), rcap_len)); } } softc->disk->d_sectorsize = softc->params.secsize; softc->disk->d_mediasize = softc->params.secsize * (off_t)softc->params.sectors; softc->disk->d_stripesize = softc->params.stripesize; softc->disk->d_stripeoffset = softc->params.stripeoffset; /* XXX: these are not actually "firmware" values, so they may be wrong */ softc->disk->d_fwsectors = softc->params.secs_per_track; softc->disk->d_fwheads = softc->params.heads; softc->disk->d_devstat->block_size = softc->params.secsize; softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE; error = disk_resize(softc->disk, M_NOWAIT); if (error != 0) xpt_print(periph->path, "disk_resize(9) failed, error = %d\n", error); } static void dasendorderedtag(void *arg) { struct da_softc *softc = arg; if (da_send_ordered) { if (!LIST_EMPTY(&softc->pending_ccbs)) { if ((softc->flags & DA_FLAG_WAS_OTAG) == 0) softc->flags |= DA_FLAG_NEED_OTAG; softc->flags &= ~DA_FLAG_WAS_OTAG; } } /* Queue us up again */ callout_reset(&softc->sendordered_c, (da_default_timeout * hz) / DA_ORDEREDTAG_INTERVAL, dasendorderedtag, softc); } /* * Step through all DA peripheral drivers, and if the device is still open, * sync the disk cache to physical media. */ static void dashutdown(void * arg, int howto) { struct cam_periph *periph; struct da_softc *softc; union ccb *ccb; int error; CAM_PERIPH_FOREACH(periph, &dadriver) { softc = (struct da_softc *)periph->softc; if (SCHEDULER_STOPPED()) { /* If we paniced with the lock held, do not recurse. */ if (!cam_periph_owned(periph) && (softc->flags & DA_FLAG_OPEN)) { dadump(softc->disk, NULL, 0, 0, 0); } continue; } cam_periph_lock(periph); /* * We only sync the cache if the drive is still open, and * if the drive is capable of it.. */ if (((softc->flags & DA_FLAG_OPEN) == 0) || (softc->quirks & DA_Q_NO_SYNC_CACHE)) { cam_periph_unlock(periph); continue; } ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); scsi_synchronize_cache(&ccb->csio, /*retries*/0, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0, /* whole disk */ /*lb_count*/0, SSD_FULL_SIZE, 60 * 60 * 1000); error = cam_periph_runccb(ccb, daerror, /*cam_flags*/0, /*sense_flags*/ SF_NO_RECOVERY | SF_NO_RETRY | SF_QUIET_IR, softc->disk->d_devstat); if (error != 0) xpt_print(periph->path, "Synchronize cache failed\n"); xpt_release_ccb(ccb); cam_periph_unlock(periph); } } #else /* !_KERNEL */ /* * XXX These are only left out of the kernel build to silence warnings. If, * for some reason these functions are used in the kernel, the ifdefs should * be moved so they are included both in the kernel and userland. */ void scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, u_int16_t ileave, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_format_unit *scsi_cmd; scsi_cmd = (struct scsi_format_unit *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = FORMAT_UNIT; scsi_cmd->byte2 = byte2; scsi_ulto2b(ileave, scsi_cmd->interleave); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ (dxfer_len > 0) ? CAM_DIR_OUT : CAM_DIR_NONE, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_read_defects(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint8_t list_format, uint32_t addr_desc_index, uint8_t *data_ptr, uint32_t dxfer_len, int minimum_cmd_size, uint8_t sense_len, uint32_t timeout) { uint8_t cdb_len; /* * These conditions allow using the 10 byte command. Otherwise we * need to use the 12 byte command. */ if ((minimum_cmd_size <= 10) && (addr_desc_index == 0) && (dxfer_len <= SRDD10_MAX_LENGTH)) { struct scsi_read_defect_data_10 *cdb10; cdb10 = (struct scsi_read_defect_data_10 *) &csio->cdb_io.cdb_bytes; cdb_len = sizeof(*cdb10); bzero(cdb10, cdb_len); cdb10->opcode = READ_DEFECT_DATA_10; cdb10->format = list_format; scsi_ulto2b(dxfer_len, cdb10->alloc_length); } else { struct scsi_read_defect_data_12 *cdb12; cdb12 = (struct scsi_read_defect_data_12 *) &csio->cdb_io.cdb_bytes; cdb_len = sizeof(*cdb12); bzero(cdb12, cdb_len); cdb12->opcode = READ_DEFECT_DATA_12; cdb12->format = list_format; scsi_ulto4b(dxfer_len, cdb12->alloc_length); scsi_ulto4b(addr_desc_index, cdb12->address_descriptor_index); } cam_fill_csio(csio, retries, cbfcnp, /*flags*/ CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, cdb_len, timeout); } void scsi_sanitize(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, u_int16_t control, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_sanitize *scsi_cmd; scsi_cmd = (struct scsi_sanitize *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = SANITIZE; scsi_cmd->byte2 = byte2; scsi_cmd->control = control; scsi_ulto2b(dxfer_len, scsi_cmd->length); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ (dxfer_len > 0) ? CAM_DIR_OUT : CAM_DIR_NONE, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } #endif /* _KERNEL */ Index: projects/building-blocks/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c =================================================================== --- projects/building-blocks/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c (revision 278195) +++ projects/building-blocks/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c (revision 278196) @@ -1,17951 +1,17981 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * $FreeBSD$ */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2014 by Delphix. All rights reserved. */ /* * DTrace - Dynamic Tracing for Solaris * * This is the implementation of the Solaris Dynamic Tracing framework * (DTrace). The user-visible interface to DTrace is described at length in * the "Solaris Dynamic Tracing Guide". The interfaces between the libdtrace * library, the in-kernel DTrace framework, and the DTrace providers are * described in the block comments in the header file. The * internal architecture of DTrace is described in the block comments in the * header file. The comments contained within the DTrace * implementation very much assume mastery of all of these sources; if one has * an unanswered question about the implementation, one should consult them * first. * * The functions here are ordered roughly as follows: * * - Probe context functions * - Probe hashing functions * - Non-probe context utility functions * - Matching functions * - Provider-to-Framework API functions * - Probe management functions * - DIF object functions * - Format functions * - Predicate functions * - ECB functions * - Buffer functions * - Enabling functions * - DOF functions * - Anonymous enabling functions * - Consumer state functions * - Helper functions * - Hook functions * - Driver cookbook functions * * Each group of functions begins with a block comment labelled the "DTrace * [Group] Functions", allowing one to find each block by searching forward * on capital-f functions. */ #include #ifndef illumos #include #endif #include #include #include #include #ifdef illumos #include #include #endif #include #include #ifdef illumos #include #endif #include #include #include #include #ifdef illumos #include #include #endif #include #ifdef illumos #include #include #endif #include #ifdef illumos #include #include #endif #include #ifdef illumos #include #include #endif #include #include #include #include "strtolctype.h" /* FreeBSD includes: */ #ifndef illumos #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dtrace_cddl.h" #include "dtrace_debug.c" #endif /* * DTrace Tunable Variables * * The following variables may be tuned by adding a line to /etc/system that * includes both the name of the DTrace module ("dtrace") and the name of the * variable. For example: * * set dtrace:dtrace_destructive_disallow = 1 * * In general, the only variables that one should be tuning this way are those * that affect system-wide DTrace behavior, and for which the default behavior * is undesirable. Most of these variables are tunable on a per-consumer * basis using DTrace options, and need not be tuned on a system-wide basis. * When tuning these variables, avoid pathological values; while some attempt * is made to verify the integrity of these variables, they are not considered * part of the supported interface to DTrace, and they are therefore not * checked comprehensively. Further, these variables should not be tuned * dynamically via "mdb -kw" or other means; they should only be tuned via * /etc/system. */ int dtrace_destructive_disallow = 0; dtrace_optval_t dtrace_nonroot_maxsize = (16 * 1024 * 1024); size_t dtrace_difo_maxsize = (256 * 1024); dtrace_optval_t dtrace_dof_maxsize = (8 * 1024 * 1024); size_t dtrace_global_maxsize = (16 * 1024); size_t dtrace_actions_max = (16 * 1024); size_t dtrace_retain_max = 1024; dtrace_optval_t dtrace_helper_actions_max = 128; dtrace_optval_t dtrace_helper_providers_max = 32; dtrace_optval_t dtrace_dstate_defsize = (1 * 1024 * 1024); size_t dtrace_strsize_default = 256; dtrace_optval_t dtrace_cleanrate_default = 9900990; /* 101 hz */ dtrace_optval_t dtrace_cleanrate_min = 200000; /* 5000 hz */ dtrace_optval_t dtrace_cleanrate_max = (uint64_t)60 * NANOSEC; /* 1/minute */ dtrace_optval_t dtrace_aggrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_statusrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_statusrate_max = (hrtime_t)10 * NANOSEC; /* 6/minute */ dtrace_optval_t dtrace_switchrate_default = NANOSEC; /* 1 hz */ dtrace_optval_t dtrace_nspec_default = 1; dtrace_optval_t dtrace_specsize_default = 32 * 1024; dtrace_optval_t dtrace_stackframes_default = 20; dtrace_optval_t dtrace_ustackframes_default = 20; dtrace_optval_t dtrace_jstackframes_default = 50; dtrace_optval_t dtrace_jstackstrsize_default = 512; int dtrace_msgdsize_max = 128; hrtime_t dtrace_chill_max = MSEC2NSEC(500); /* 500 ms */ hrtime_t dtrace_chill_interval = NANOSEC; /* 1000 ms */ int dtrace_devdepth_max = 32; int dtrace_err_verbose; hrtime_t dtrace_deadman_interval = NANOSEC; hrtime_t dtrace_deadman_timeout = (hrtime_t)10 * NANOSEC; hrtime_t dtrace_deadman_user = (hrtime_t)30 * NANOSEC; hrtime_t dtrace_unregister_defunct_reap = (hrtime_t)60 * NANOSEC; #ifndef illumos int dtrace_memstr_max = 4096; #endif /* * DTrace External Variables * * As dtrace(7D) is a kernel module, any DTrace variables are obviously * available to DTrace consumers via the backtick (`) syntax. One of these, * dtrace_zero, is made deliberately so: it is provided as a source of * well-known, zero-filled memory. While this variable is not documented, * it is used by some translators as an implementation detail. */ const char dtrace_zero[256] = { 0 }; /* zero-filled memory */ /* * DTrace Internal Variables */ #ifdef illumos static dev_info_t *dtrace_devi; /* device info */ #endif #ifdef illumos static vmem_t *dtrace_arena; /* probe ID arena */ static vmem_t *dtrace_minor; /* minor number arena */ #else static taskq_t *dtrace_taskq; /* task queue */ static struct unrhdr *dtrace_arena; /* Probe ID number. */ #endif static dtrace_probe_t **dtrace_probes; /* array of all probes */ static int dtrace_nprobes; /* number of probes */ static dtrace_provider_t *dtrace_provider; /* provider list */ static dtrace_meta_t *dtrace_meta_pid; /* user-land meta provider */ static int dtrace_opens; /* number of opens */ static int dtrace_helpers; /* number of helpers */ static int dtrace_getf; /* number of unpriv getf()s */ #ifdef illumos static void *dtrace_softstate; /* softstate pointer */ #endif static dtrace_hash_t *dtrace_bymod; /* probes hashed by module */ static dtrace_hash_t *dtrace_byfunc; /* probes hashed by function */ static dtrace_hash_t *dtrace_byname; /* probes hashed by name */ static dtrace_toxrange_t *dtrace_toxrange; /* toxic range array */ static int dtrace_toxranges; /* number of toxic ranges */ static int dtrace_toxranges_max; /* size of toxic range array */ static dtrace_anon_t dtrace_anon; /* anonymous enabling */ static kmem_cache_t *dtrace_state_cache; /* cache for dynamic state */ static uint64_t dtrace_vtime_references; /* number of vtimestamp refs */ static kthread_t *dtrace_panicked; /* panicking thread */ static dtrace_ecb_t *dtrace_ecb_create_cache; /* cached created ECB */ static dtrace_genid_t dtrace_probegen; /* current probe generation */ static dtrace_helpers_t *dtrace_deferred_pid; /* deferred helper list */ static dtrace_enabling_t *dtrace_retained; /* list of retained enablings */ static dtrace_genid_t dtrace_retained_gen; /* current retained enab gen */ static dtrace_dynvar_t dtrace_dynhash_sink; /* end of dynamic hash chains */ static int dtrace_dynvar_failclean; /* dynvars failed to clean */ #ifndef illumos static struct mtx dtrace_unr_mtx; MTX_SYSINIT(dtrace_unr_mtx, &dtrace_unr_mtx, "Unique resource identifier", MTX_DEF); int dtrace_in_probe; /* non-zero if executing a probe */ #if defined(__i386__) || defined(__amd64__) || defined(__mips__) || defined(__powerpc__) uintptr_t dtrace_in_probe_addr; /* Address of invop when already in probe */ #endif static eventhandler_tag dtrace_kld_load_tag; static eventhandler_tag dtrace_kld_unload_try_tag; #endif /* * DTrace Locking * DTrace is protected by three (relatively coarse-grained) locks: * * (1) dtrace_lock is required to manipulate essentially any DTrace state, * including enabling state, probes, ECBs, consumer state, helper state, * etc. Importantly, dtrace_lock is _not_ required when in probe context; * probe context is lock-free -- synchronization is handled via the * dtrace_sync() cross call mechanism. * * (2) dtrace_provider_lock is required when manipulating provider state, or * when provider state must be held constant. * * (3) dtrace_meta_lock is required when manipulating meta provider state, or * when meta provider state must be held constant. * * The lock ordering between these three locks is dtrace_meta_lock before * dtrace_provider_lock before dtrace_lock. (In particular, there are * several places where dtrace_provider_lock is held by the framework as it * calls into the providers -- which then call back into the framework, * grabbing dtrace_lock.) * * There are two other locks in the mix: mod_lock and cpu_lock. With respect * to dtrace_provider_lock and dtrace_lock, cpu_lock continues its historical * role as a coarse-grained lock; it is acquired before both of these locks. * With respect to dtrace_meta_lock, its behavior is stranger: cpu_lock must * be acquired _between_ dtrace_meta_lock and any other DTrace locks. * mod_lock is similar with respect to dtrace_provider_lock in that it must be * acquired _between_ dtrace_provider_lock and dtrace_lock. */ static kmutex_t dtrace_lock; /* probe state lock */ static kmutex_t dtrace_provider_lock; /* provider state lock */ static kmutex_t dtrace_meta_lock; /* meta-provider state lock */ #ifndef illumos /* XXX FreeBSD hacks. */ #define cr_suid cr_svuid #define cr_sgid cr_svgid #define ipaddr_t in_addr_t #define mod_modname pathname #define vuprintf vprintf #define ttoproc(_a) ((_a)->td_proc) #define crgetzoneid(_a) 0 #define NCPU MAXCPU #define SNOCD 0 #define CPU_ON_INTR(_a) 0 #define PRIV_EFFECTIVE (1 << 0) #define PRIV_DTRACE_KERNEL (1 << 1) #define PRIV_DTRACE_PROC (1 << 2) #define PRIV_DTRACE_USER (1 << 3) #define PRIV_PROC_OWNER (1 << 4) #define PRIV_PROC_ZONE (1 << 5) #define PRIV_ALL ~0 SYSCTL_DECL(_debug_dtrace); SYSCTL_DECL(_kern_dtrace); #endif #ifdef illumos #define curcpu CPU->cpu_id #endif /* * DTrace Provider Variables * * These are the variables relating to DTrace as a provider (that is, the * provider of the BEGIN, END, and ERROR probes). */ static dtrace_pattr_t dtrace_provider_attr = { { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON }, }; static void dtrace_nullop(void) {} static dtrace_pops_t dtrace_provider_ops = { (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop, (void (*)(void *, modctl_t *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, NULL, NULL, NULL, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop }; static dtrace_id_t dtrace_probeid_begin; /* special BEGIN probe */ static dtrace_id_t dtrace_probeid_end; /* special END probe */ dtrace_id_t dtrace_probeid_error; /* special ERROR probe */ /* * DTrace Helper Tracing Variables + * + * These variables should be set dynamically to enable helper tracing. The + * only variables that should be set are dtrace_helptrace_enable (which should + * be set to a non-zero value to allocate helper tracing buffers on the next + * open of /dev/dtrace) and dtrace_helptrace_disable (which should be set to a + * non-zero value to deallocate helper tracing buffers on the next close of + * /dev/dtrace). When (and only when) helper tracing is disabled, the + * buffer size may also be set via dtrace_helptrace_bufsize. */ -uint32_t dtrace_helptrace_next = 0; -uint32_t dtrace_helptrace_nlocals; -char *dtrace_helptrace_buffer; -int dtrace_helptrace_bufsize = 512 * 1024; +int dtrace_helptrace_enable = 0; +int dtrace_helptrace_disable = 0; +int dtrace_helptrace_bufsize = 16 * 1024 * 1024; +uint32_t dtrace_helptrace_nlocals; +static dtrace_helptrace_t *dtrace_helptrace_buffer; +static uint32_t dtrace_helptrace_next = 0; +static int dtrace_helptrace_wrapped = 0; -#ifdef DEBUG -int dtrace_helptrace_enabled = 1; -#else -int dtrace_helptrace_enabled = 0; -#endif - /* * DTrace Error Hashing * * On DEBUG kernels, DTrace will track the errors that has seen in a hash * table. This is very useful for checking coverage of tests that are * expected to induce DIF or DOF processing errors, and may be useful for * debugging problems in the DIF code generator or in DOF generation . The * error hash may be examined with the ::dtrace_errhash MDB dcmd. */ #ifdef DEBUG static dtrace_errhash_t dtrace_errhash[DTRACE_ERRHASHSZ]; static const char *dtrace_errlast; static kthread_t *dtrace_errthread; static kmutex_t dtrace_errlock; #endif /* * DTrace Macros and Constants * * These are various macros that are useful in various spots in the * implementation, along with a few random constants that have no meaning * outside of the implementation. There is no real structure to this cpp * mishmash -- but is there ever? */ #define DTRACE_HASHSTR(hash, probe) \ dtrace_hash_str(*((char **)((uintptr_t)(probe) + (hash)->dth_stroffs))) #define DTRACE_HASHNEXT(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_nextoffs) #define DTRACE_HASHPREV(hash, probe) \ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_prevoffs) #define DTRACE_HASHEQ(hash, lhs, rhs) \ (strcmp(*((char **)((uintptr_t)(lhs) + (hash)->dth_stroffs)), \ *((char **)((uintptr_t)(rhs) + (hash)->dth_stroffs))) == 0) #define DTRACE_AGGHASHSIZE_SLEW 17 #define DTRACE_V4MAPPED_OFFSET (sizeof (uint32_t) * 3) /* * The key for a thread-local variable consists of the lower 61 bits of the * t_did, plus the 3 bits of the highest active interrupt above LOCK_LEVEL. * We add DIF_VARIABLE_MAX to t_did to assure that the thread key is never * equal to a variable identifier. This is necessary (but not sufficient) to * assure that global associative arrays never collide with thread-local * variables. To guarantee that they cannot collide, we must also define the * order for keying dynamic variables. That order is: * * [ key0 ] ... [ keyn ] [ variable-key ] [ tls-key ] * * Because the variable-key and the tls-key are in orthogonal spaces, there is * no way for a global variable key signature to match a thread-local key * signature. */ #ifdef illumos #define DTRACE_TLS_THRKEY(where) { \ uint_t intr = 0; \ uint_t actv = CPU->cpu_intr_actv >> (LOCK_LEVEL + 1); \ for (; actv; actv >>= 1) \ intr++; \ ASSERT(intr < (1 << 3)); \ (where) = ((curthread->t_did + DIF_VARIABLE_MAX) & \ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \ } #else #define DTRACE_TLS_THRKEY(where) { \ solaris_cpu_t *_c = &solaris_cpu[curcpu]; \ uint_t intr = 0; \ uint_t actv = _c->cpu_intr_actv; \ for (; actv; actv >>= 1) \ intr++; \ ASSERT(intr < (1 << 3)); \ (where) = ((curthread->td_tid + DIF_VARIABLE_MAX) & \ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \ } #endif #define DT_BSWAP_8(x) ((x) & 0xff) #define DT_BSWAP_16(x) ((DT_BSWAP_8(x) << 8) | DT_BSWAP_8((x) >> 8)) #define DT_BSWAP_32(x) ((DT_BSWAP_16(x) << 16) | DT_BSWAP_16((x) >> 16)) #define DT_BSWAP_64(x) ((DT_BSWAP_32(x) << 32) | DT_BSWAP_32((x) >> 32)) #define DT_MASK_LO 0x00000000FFFFFFFFULL #define DTRACE_STORE(type, tomax, offset, what) \ *((type *)((uintptr_t)(tomax) + (uintptr_t)offset)) = (type)(what); #ifndef __x86 #define DTRACE_ALIGNCHECK(addr, size, flags) \ if (addr & (size - 1)) { \ *flags |= CPU_DTRACE_BADALIGN; \ cpu_core[curcpu].cpuc_dtrace_illval = addr; \ return (0); \ } #else #define DTRACE_ALIGNCHECK(addr, size, flags) #endif /* * Test whether a range of memory starting at testaddr of size testsz falls * within the range of memory described by addr, sz. We take care to avoid * problems with overflow and underflow of the unsigned quantities, and * disallow all negative sizes. Ranges of size 0 are allowed. */ #define DTRACE_INRANGE(testaddr, testsz, baseaddr, basesz) \ ((testaddr) - (uintptr_t)(baseaddr) < (basesz) && \ (testaddr) + (testsz) - (uintptr_t)(baseaddr) <= (basesz) && \ (testaddr) + (testsz) >= (testaddr)) /* * Test whether alloc_sz bytes will fit in the scratch region. We isolate * alloc_sz on the righthand side of the comparison in order to avoid overflow * or underflow in the comparison with it. This is simpler than the INRANGE * check above, because we know that the dtms_scratch_ptr is valid in the * range. Allocations of size zero are allowed. */ #define DTRACE_INSCRATCH(mstate, alloc_sz) \ ((mstate)->dtms_scratch_base + (mstate)->dtms_scratch_size - \ (mstate)->dtms_scratch_ptr >= (alloc_sz)) #define DTRACE_LOADFUNC(bits) \ /*CSTYLED*/ \ uint##bits##_t \ dtrace_load##bits(uintptr_t addr) \ { \ size_t size = bits / NBBY; \ /*CSTYLED*/ \ uint##bits##_t rval; \ int i; \ volatile uint16_t *flags = (volatile uint16_t *) \ &cpu_core[curcpu].cpuc_dtrace_flags; \ \ DTRACE_ALIGNCHECK(addr, size, flags); \ \ for (i = 0; i < dtrace_toxranges; i++) { \ if (addr >= dtrace_toxrange[i].dtt_limit) \ continue; \ \ if (addr + size <= dtrace_toxrange[i].dtt_base) \ continue; \ \ /* \ * This address falls within a toxic region; return 0. \ */ \ *flags |= CPU_DTRACE_BADADDR; \ cpu_core[curcpu].cpuc_dtrace_illval = addr; \ return (0); \ } \ \ *flags |= CPU_DTRACE_NOFAULT; \ /*CSTYLED*/ \ rval = *((volatile uint##bits##_t *)addr); \ *flags &= ~CPU_DTRACE_NOFAULT; \ \ return (!(*flags & CPU_DTRACE_FAULT) ? rval : 0); \ } #ifdef _LP64 #define dtrace_loadptr dtrace_load64 #else #define dtrace_loadptr dtrace_load32 #endif #define DTRACE_DYNHASH_FREE 0 #define DTRACE_DYNHASH_SINK 1 #define DTRACE_DYNHASH_VALID 2 #define DTRACE_MATCH_NEXT 0 #define DTRACE_MATCH_DONE 1 #define DTRACE_ANCHORED(probe) ((probe)->dtpr_func[0] != '\0') #define DTRACE_STATE_ALIGN 64 #define DTRACE_FLAGS2FLT(flags) \ (((flags) & CPU_DTRACE_BADADDR) ? DTRACEFLT_BADADDR : \ ((flags) & CPU_DTRACE_ILLOP) ? DTRACEFLT_ILLOP : \ ((flags) & CPU_DTRACE_DIVZERO) ? DTRACEFLT_DIVZERO : \ ((flags) & CPU_DTRACE_KPRIV) ? DTRACEFLT_KPRIV : \ ((flags) & CPU_DTRACE_UPRIV) ? DTRACEFLT_UPRIV : \ ((flags) & CPU_DTRACE_TUPOFLOW) ? DTRACEFLT_TUPOFLOW : \ ((flags) & CPU_DTRACE_BADALIGN) ? DTRACEFLT_BADALIGN : \ ((flags) & CPU_DTRACE_NOSCRATCH) ? DTRACEFLT_NOSCRATCH : \ ((flags) & CPU_DTRACE_BADSTACK) ? DTRACEFLT_BADSTACK : \ DTRACEFLT_UNKNOWN) #define DTRACEACT_ISSTRING(act) \ ((act)->dta_kind == DTRACEACT_DIFEXPR && \ (act)->dta_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) /* Function prototype definitions: */ static size_t dtrace_strlen(const char *, size_t); static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id); static void dtrace_enabling_provide(dtrace_provider_t *); static int dtrace_enabling_match(dtrace_enabling_t *, int *); static void dtrace_enabling_matchall(void); static void dtrace_enabling_reap(void); static dtrace_state_t *dtrace_anon_grab(void); static uint64_t dtrace_helper(int, dtrace_mstate_t *, dtrace_state_t *, uint64_t, uint64_t); static dtrace_helpers_t *dtrace_helpers_create(proc_t *); static void dtrace_buffer_drop(dtrace_buffer_t *); static int dtrace_buffer_consumed(dtrace_buffer_t *, hrtime_t when); static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *, size_t, size_t, dtrace_state_t *, dtrace_mstate_t *); static int dtrace_state_option(dtrace_state_t *, dtrace_optid_t, dtrace_optval_t); static int dtrace_ecb_create_enable(dtrace_probe_t *, void *); static void dtrace_helper_provider_destroy(dtrace_helper_provider_t *); uint16_t dtrace_load16(uintptr_t); uint32_t dtrace_load32(uintptr_t); uint64_t dtrace_load64(uintptr_t); uint8_t dtrace_load8(uintptr_t); void dtrace_dynvar_clean(dtrace_dstate_t *); dtrace_dynvar_t *dtrace_dynvar(dtrace_dstate_t *, uint_t, dtrace_key_t *, size_t, dtrace_dynvar_op_t, dtrace_mstate_t *, dtrace_vstate_t *); uintptr_t dtrace_dif_varstr(uintptr_t, dtrace_state_t *, dtrace_mstate_t *); static int dtrace_priv_proc(dtrace_state_t *); static void dtrace_getf_barrier(void); /* * DTrace Probe Context Functions * * These functions are called from probe context. Because probe context is * any context in which C may be called, arbitrarily locks may be held, * interrupts may be disabled, we may be in arbitrary dispatched state, etc. * As a result, functions called from probe context may only call other DTrace * support functions -- they may not interact at all with the system at large. * (Note that the ASSERT macro is made probe-context safe by redefining it in * terms of dtrace_assfail(), a probe-context safe function.) If arbitrary * loads are to be performed from probe context, they _must_ be in terms of * the safe dtrace_load*() variants. * * Some functions in this block are not actually called from probe context; * for these functions, there will be a comment above the function reading * "Note: not called from probe context." */ void dtrace_panic(const char *format, ...) { va_list alist; va_start(alist, format); dtrace_vpanic(format, alist); va_end(alist); } int dtrace_assfail(const char *a, const char *f, int l) { dtrace_panic("assertion failed: %s, file: %s, line: %d", a, f, l); /* * We just need something here that even the most clever compiler * cannot optimize away. */ return (a[(uintptr_t)f]); } /* * Atomically increment a specified error counter from probe context. */ static void dtrace_error(uint32_t *counter) { /* * Most counters stored to in probe context are per-CPU counters. * However, there are some error conditions that are sufficiently * arcane that they don't merit per-CPU storage. If these counters * are incremented concurrently on different CPUs, scalability will be * adversely affected -- but we don't expect them to be white-hot in a * correctly constructed enabling... */ uint32_t oval, nval; do { oval = *counter; if ((nval = oval + 1) == 0) { /* * If the counter would wrap, set it to 1 -- assuring * that the counter is never zero when we have seen * errors. (The counter must be 32-bits because we * aren't guaranteed a 64-bit compare&swap operation.) * To save this code both the infamy of being fingered * by a priggish news story and the indignity of being * the target of a neo-puritan witch trial, we're * carefully avoiding any colorful description of the * likelihood of this condition -- but suffice it to * say that it is only slightly more likely than the * overflow of predicate cache IDs, as discussed in * dtrace_predicate_create(). */ nval = 1; } } while (dtrace_cas32(counter, oval, nval) != oval); } /* * Use the DTRACE_LOADFUNC macro to define functions for each of loading a * uint8_t, a uint16_t, a uint32_t and a uint64_t. */ DTRACE_LOADFUNC(8) DTRACE_LOADFUNC(16) DTRACE_LOADFUNC(32) DTRACE_LOADFUNC(64) static int dtrace_inscratch(uintptr_t dest, size_t size, dtrace_mstate_t *mstate) { if (dest < mstate->dtms_scratch_base) return (0); if (dest + size < dest) return (0); if (dest + size > mstate->dtms_scratch_ptr) return (0); return (1); } static int dtrace_canstore_statvar(uint64_t addr, size_t sz, dtrace_statvar_t **svars, int nsvars) { int i; for (i = 0; i < nsvars; i++) { dtrace_statvar_t *svar = svars[i]; if (svar == NULL || svar->dtsv_size == 0) continue; if (DTRACE_INRANGE(addr, sz, svar->dtsv_data, svar->dtsv_size)) return (1); } return (0); } /* * Check to see if the address is within a memory region to which a store may * be issued. This includes the DTrace scratch areas, and any DTrace variable * region. The caller of dtrace_canstore() is responsible for performing any * alignment checks that are needed before stores are actually executed. */ static int dtrace_canstore(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { /* * First, check to see if the address is in scratch space... */ if (DTRACE_INRANGE(addr, sz, mstate->dtms_scratch_base, mstate->dtms_scratch_size)) return (1); /* * Now check to see if it's a dynamic variable. This check will pick * up both thread-local variables and any global dynamically-allocated * variables. */ if (DTRACE_INRANGE(addr, sz, vstate->dtvs_dynvars.dtds_base, vstate->dtvs_dynvars.dtds_size)) { dtrace_dstate_t *dstate = &vstate->dtvs_dynvars; uintptr_t base = (uintptr_t)dstate->dtds_base + (dstate->dtds_hashsize * sizeof (dtrace_dynhash_t)); uintptr_t chunkoffs; /* * Before we assume that we can store here, we need to make * sure that it isn't in our metadata -- storing to our * dynamic variable metadata would corrupt our state. For * the range to not include any dynamic variable metadata, * it must: * * (1) Start above the hash table that is at the base of * the dynamic variable space * * (2) Have a starting chunk offset that is beyond the * dtrace_dynvar_t that is at the base of every chunk * * (3) Not span a chunk boundary * */ if (addr < base) return (0); chunkoffs = (addr - base) % dstate->dtds_chunksize; if (chunkoffs < sizeof (dtrace_dynvar_t)) return (0); if (chunkoffs + sz > dstate->dtds_chunksize) return (0); return (1); } /* * Finally, check the static local and global variables. These checks * take the longest, so we perform them last. */ if (dtrace_canstore_statvar(addr, sz, vstate->dtvs_locals, vstate->dtvs_nlocals)) return (1); if (dtrace_canstore_statvar(addr, sz, vstate->dtvs_globals, vstate->dtvs_nglobals)) return (1); return (0); } /* * Convenience routine to check to see if the address is within a memory * region in which a load may be issued given the user's privilege level; * if not, it sets the appropriate error flags and loads 'addr' into the * illegal value slot. * * DTrace subroutines (DIF_SUBR_*) should use this helper to implement * appropriate memory access protection. */ static int dtrace_canload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; file_t *fp; /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); /* * You can obviously read that which you can store. */ if (dtrace_canstore(addr, sz, mstate, vstate)) return (1); /* * We're allowed to read from our own string table. */ if (DTRACE_INRANGE(addr, sz, mstate->dtms_difo->dtdo_strtab, mstate->dtms_difo->dtdo_strlen)) return (1); if (vstate->dtvs_state != NULL && dtrace_priv_proc(vstate->dtvs_state)) { proc_t *p; /* * When we have privileges to the current process, there are * several context-related kernel structures that are safe to * read, even absent the privilege to read from kernel memory. * These reads are safe because these structures contain only * state that (1) we're permitted to read, (2) is harmless or * (3) contains pointers to additional kernel state that we're * not permitted to read (and as such, do not present an * opportunity for privilege escalation). Finally (and * critically), because of the nature of their relation with * the current thread context, the memory associated with these * structures cannot change over the duration of probe context, * and it is therefore impossible for this memory to be * deallocated and reallocated as something else while it's * being operated upon. */ if (DTRACE_INRANGE(addr, sz, curthread, sizeof (kthread_t))) return (1); if ((p = curthread->t_procp) != NULL && DTRACE_INRANGE(addr, sz, curthread->t_procp, sizeof (proc_t))) { return (1); } if (curthread->t_cred != NULL && DTRACE_INRANGE(addr, sz, curthread->t_cred, sizeof (cred_t))) { return (1); } #ifdef illumos if (p != NULL && p->p_pidp != NULL && DTRACE_INRANGE(addr, sz, &(p->p_pidp->pid_id), sizeof (pid_t))) { return (1); } if (curthread->t_cpu != NULL && DTRACE_INRANGE(addr, sz, curthread->t_cpu, offsetof(cpu_t, cpu_pause_thread))) { return (1); } #endif } if ((fp = mstate->dtms_getf) != NULL) { uintptr_t psz = sizeof (void *); vnode_t *vp; vnodeops_t *op; /* * When getf() returns a file_t, the enabling is implicitly * granted the (transient) right to read the returned file_t * as well as the v_path and v_op->vnop_name of the underlying * vnode. These accesses are allowed after a successful * getf() because the members that they refer to cannot change * once set -- and the barrier logic in the kernel's closef() * path assures that the file_t and its referenced vode_t * cannot themselves be stale (that is, it impossible for * either dtms_getf itself or its f_vnode member to reference * freed memory). */ if (DTRACE_INRANGE(addr, sz, fp, sizeof (file_t))) return (1); if ((vp = fp->f_vnode) != NULL) { #ifdef illumos if (DTRACE_INRANGE(addr, sz, &vp->v_path, psz)) return (1); if (vp->v_path != NULL && DTRACE_INRANGE(addr, sz, vp->v_path, strlen(vp->v_path) + 1)) { return (1); } #endif if (DTRACE_INRANGE(addr, sz, &vp->v_op, psz)) return (1); #ifdef illumos if ((op = vp->v_op) != NULL && DTRACE_INRANGE(addr, sz, &op->vnop_name, psz)) { return (1); } if (op != NULL && op->vnop_name != NULL && DTRACE_INRANGE(addr, sz, op->vnop_name, strlen(op->vnop_name) + 1)) { return (1); } #endif } } DTRACE_CPUFLAG_SET(CPU_DTRACE_KPRIV); *illval = addr; return (0); } /* * Convenience routine to check to see if a given string is within a memory * region in which a load may be issued given the user's privilege level; * this exists so that we don't need to issue unnecessary dtrace_strlen() * calls in the event that the user has all privileges. */ static int dtrace_strcanload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { size_t strsz; /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); strsz = 1 + dtrace_strlen((char *)(uintptr_t)addr, sz); if (dtrace_canload(addr, strsz, mstate, vstate)) return (1); return (0); } /* * Convenience routine to check to see if a given variable is within a memory * region in which a load may be issued given the user's privilege level. */ static int dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { size_t sz; ASSERT(type->dtdt_flags & DIF_TF_BYREF); /* * If we hold the privilege to read from kernel memory, then * everything is readable. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (1); if (type->dtdt_kind == DIF_TYPE_STRING) sz = dtrace_strlen(src, vstate->dtvs_state->dts_options[DTRACEOPT_STRSIZE]) + 1; else sz = type->dtdt_size; return (dtrace_canload((uintptr_t)src, sz, mstate, vstate)); } /* * Convert a string to a signed integer using safe loads. * * NOTE: This function uses various macros from strtolctype.h to manipulate * digit values, etc -- these have all been checked to ensure they make * no additional function calls. */ static int64_t dtrace_strtoll(char *input, int base, size_t limit) { uintptr_t pos = (uintptr_t)input; int64_t val = 0; int x; boolean_t neg = B_FALSE; char c, cc, ccc; uintptr_t end = pos + limit; /* * Consume any whitespace preceding digits. */ while ((c = dtrace_load8(pos)) == ' ' || c == '\t') pos++; /* * Handle an explicit sign if one is present. */ if (c == '-' || c == '+') { if (c == '-') neg = B_TRUE; c = dtrace_load8(++pos); } /* * Check for an explicit hexadecimal prefix ("0x" or "0X") and skip it * if present. */ if (base == 16 && c == '0' && ((cc = dtrace_load8(pos + 1)) == 'x' || cc == 'X') && isxdigit(ccc = dtrace_load8(pos + 2))) { pos += 2; c = ccc; } /* * Read in contiguous digits until the first non-digit character. */ for (; pos < end && c != '\0' && lisalnum(c) && (x = DIGIT(c)) < base; c = dtrace_load8(++pos)) val = val * base + x; return (neg ? -val : val); } /* * Compare two strings using safe loads. */ static int dtrace_strncmp(char *s1, char *s2, size_t limit) { uint8_t c1, c2; volatile uint16_t *flags; if (s1 == s2 || limit == 0) return (0); flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; do { if (s1 == NULL) { c1 = '\0'; } else { c1 = dtrace_load8((uintptr_t)s1++); } if (s2 == NULL) { c2 = '\0'; } else { c2 = dtrace_load8((uintptr_t)s2++); } if (c1 != c2) return (c1 - c2); } while (--limit && c1 != '\0' && !(*flags & CPU_DTRACE_FAULT)); return (0); } /* * Compute strlen(s) for a string using safe memory accesses. The additional * len parameter is used to specify a maximum length to ensure completion. */ static size_t dtrace_strlen(const char *s, size_t lim) { uint_t len; for (len = 0; len != lim; len++) { if (dtrace_load8((uintptr_t)s++) == '\0') break; } return (len); } /* * Check if an address falls within a toxic region. */ static int dtrace_istoxic(uintptr_t kaddr, size_t size) { uintptr_t taddr, tsize; int i; for (i = 0; i < dtrace_toxranges; i++) { taddr = dtrace_toxrange[i].dtt_base; tsize = dtrace_toxrange[i].dtt_limit - taddr; if (kaddr - taddr < tsize) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = kaddr; return (1); } if (taddr - kaddr < size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = taddr; return (1); } } return (0); } /* * Copy src to dst using safe memory accesses. The src is assumed to be unsafe * memory specified by the DIF program. The dst is assumed to be safe memory * that we can store to directly because it is managed by DTrace. As with * standard bcopy, overlapping copies are handled properly. */ static void dtrace_bcopy(const void *src, void *dst, size_t len) { if (len != 0) { uint8_t *s1 = dst; const uint8_t *s2 = src; if (s1 <= s2) { do { *s1++ = dtrace_load8((uintptr_t)s2++); } while (--len != 0); } else { s2 += len; s1 += len; do { *--s1 = dtrace_load8((uintptr_t)--s2); } while (--len != 0); } } } /* * Copy src to dst using safe memory accesses, up to either the specified * length, or the point that a nul byte is encountered. The src is assumed to * be unsafe memory specified by the DIF program. The dst is assumed to be * safe memory that we can store to directly because it is managed by DTrace. * Unlike dtrace_bcopy(), overlapping regions are not handled. */ static void dtrace_strcpy(const void *src, void *dst, size_t len) { if (len != 0) { uint8_t *s1 = dst, c; const uint8_t *s2 = src; do { *s1++ = c = dtrace_load8((uintptr_t)s2++); } while (--len != 0 && c != '\0'); } } /* * Copy src to dst, deriving the size and type from the specified (BYREF) * variable type. The src is assumed to be unsafe memory specified by the DIF * program. The dst is assumed to be DTrace variable memory that is of the * specified type; we assume that we can store to directly. */ static void dtrace_vcopy(void *src, void *dst, dtrace_diftype_t *type) { ASSERT(type->dtdt_flags & DIF_TF_BYREF); if (type->dtdt_kind == DIF_TYPE_STRING) { dtrace_strcpy(src, dst, type->dtdt_size); } else { dtrace_bcopy(src, dst, type->dtdt_size); } } /* * Compare s1 to s2 using safe memory accesses. The s1 data is assumed to be * unsafe memory specified by the DIF program. The s2 data is assumed to be * safe memory that we can access directly because it is managed by DTrace. */ static int dtrace_bcmp(const void *s1, const void *s2, size_t len) { volatile uint16_t *flags; flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; if (s1 == s2) return (0); if (s1 == NULL || s2 == NULL) return (1); if (s1 != s2 && len != 0) { const uint8_t *ps1 = s1; const uint8_t *ps2 = s2; do { if (dtrace_load8((uintptr_t)ps1++) != *ps2++) return (1); } while (--len != 0 && !(*flags & CPU_DTRACE_FAULT)); } return (0); } /* * Zero the specified region using a simple byte-by-byte loop. Note that this * is for safe DTrace-managed memory only. */ static void dtrace_bzero(void *dst, size_t len) { uchar_t *cp; for (cp = dst; len != 0; len--) *cp++ = 0; } static void dtrace_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) { uint64_t result[2]; result[0] = addend1[0] + addend2[0]; result[1] = addend1[1] + addend2[1] + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); sum[0] = result[0]; sum[1] = result[1]; } /* * Shift the 128-bit value in a by b. If b is positive, shift left. * If b is negative, shift right. */ static void dtrace_shift_128(uint64_t *a, int b) { uint64_t mask; if (b == 0) return; if (b < 0) { b = -b; if (b >= 64) { a[0] = a[1] >> (b - 64); a[1] = 0; } else { a[0] >>= b; mask = 1LL << (64 - b); mask -= 1; a[0] |= ((a[1] & mask) << (64 - b)); a[1] >>= b; } } else { if (b >= 64) { a[1] = a[0] << (b - 64); a[0] = 0; } else { a[1] <<= b; mask = a[0] >> (64 - b); a[1] |= mask; a[0] <<= b; } } } /* * The basic idea is to break the 2 64-bit values into 4 32-bit values, * use native multiplication on those, and then re-combine into the * resulting 128-bit value. * * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = * hi1 * hi2 << 64 + * hi1 * lo2 << 32 + * hi2 * lo1 << 32 + * lo1 * lo2 */ static void dtrace_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) { uint64_t hi1, hi2, lo1, lo2; uint64_t tmp[2]; hi1 = factor1 >> 32; hi2 = factor2 >> 32; lo1 = factor1 & DT_MASK_LO; lo2 = factor2 & DT_MASK_LO; product[0] = lo1 * lo2; product[1] = hi1 * hi2; tmp[0] = hi1 * lo2; tmp[1] = 0; dtrace_shift_128(tmp, 32); dtrace_add_128(product, tmp, product); tmp[0] = hi2 * lo1; tmp[1] = 0; dtrace_shift_128(tmp, 32); dtrace_add_128(product, tmp, product); } /* * This privilege check should be used by actions and subroutines to * verify that the user credentials of the process that enabled the * invoking ECB match the target credentials */ static int dtrace_priv_proc_common_user(dtrace_state_t *state) { cred_t *cr, *s_cr = state->dts_cred.dcr_cred; /* * We should always have a non-NULL state cred here, since if cred * is null (anonymous tracing), we fast-path bypass this routine. */ ASSERT(s_cr != NULL); if ((cr = CRED()) != NULL && s_cr->cr_uid == cr->cr_uid && s_cr->cr_uid == cr->cr_ruid && s_cr->cr_uid == cr->cr_suid && s_cr->cr_gid == cr->cr_gid && s_cr->cr_gid == cr->cr_rgid && s_cr->cr_gid == cr->cr_sgid) return (1); return (0); } /* * This privilege check should be used by actions and subroutines to * verify that the zone of the process that enabled the invoking ECB * matches the target credentials */ static int dtrace_priv_proc_common_zone(dtrace_state_t *state) { #ifdef illumos cred_t *cr, *s_cr = state->dts_cred.dcr_cred; /* * We should always have a non-NULL state cred here, since if cred * is null (anonymous tracing), we fast-path bypass this routine. */ ASSERT(s_cr != NULL); if ((cr = CRED()) != NULL && s_cr->cr_zone == cr->cr_zone) return (1); return (0); #else return (1); #endif } /* * This privilege check should be used by actions and subroutines to * verify that the process has not setuid or changed credentials. */ static int dtrace_priv_proc_common_nocd(void) { proc_t *proc; if ((proc = ttoproc(curthread)) != NULL && !(proc->p_flag & SNOCD)) return (1); return (0); } static int dtrace_priv_proc_destructive(dtrace_state_t *state) { int action = state->dts_cred.dcr_action; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE) == 0) && dtrace_priv_proc_common_zone(state) == 0) goto bad; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER) == 0) && dtrace_priv_proc_common_user(state) == 0) goto bad; if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG) == 0) && dtrace_priv_proc_common_nocd() == 0) goto bad; return (1); bad: cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_proc_control(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_PROC_CONTROL) return (1); if (dtrace_priv_proc_common_zone(state) && dtrace_priv_proc_common_user(state) && dtrace_priv_proc_common_nocd()) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_proc(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_PROC) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV; return (0); } static int dtrace_priv_kernel(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV; return (0); } static int dtrace_priv_kernel_destructive(dtrace_state_t *state) { if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL_DESTRUCTIVE) return (1); cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV; return (0); } /* * Determine if the dte_cond of the specified ECB allows for processing of * the current probe to continue. Note that this routine may allow continued * processing, but with access(es) stripped from the mstate's dtms_access * field. */ static int dtrace_priv_probe(dtrace_state_t *state, dtrace_mstate_t *mstate, dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; dtrace_provider_t *prov = probe->dtpr_provider; dtrace_pops_t *pops = &prov->dtpv_pops; int mode = DTRACE_MODE_NOPRIV_DROP; ASSERT(ecb->dte_cond); #ifdef illumos if (pops->dtps_mode != NULL) { mode = pops->dtps_mode(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); ASSERT((mode & DTRACE_MODE_USER) || (mode & DTRACE_MODE_KERNEL)); ASSERT((mode & DTRACE_MODE_NOPRIV_RESTRICT) || (mode & DTRACE_MODE_NOPRIV_DROP)); } /* * If the dte_cond bits indicate that this consumer is only allowed to * see user-mode firings of this probe, call the provider's dtps_mode() * entry point to check that the probe was fired while in a user * context. If that's not the case, use the policy specified by the * provider to determine if we drop the probe or merely restrict * operation. */ if (ecb->dte_cond & DTRACE_COND_USERMODE) { ASSERT(mode != DTRACE_MODE_NOPRIV_DROP); if (!(mode & DTRACE_MODE_USER)) { if (mode & DTRACE_MODE_NOPRIV_DROP) return (0); mstate->dtms_access &= ~DTRACE_ACCESS_ARGS; } } #endif /* * This is more subtle than it looks. We have to be absolutely certain * that CRED() isn't going to change out from under us so it's only * legit to examine that structure if we're in constrained situations. * Currently, the only times we'll this check is if a non-super-user * has enabled the profile or syscall providers -- providers that * allow visibility of all processes. For the profile case, the check * above will ensure that we're examining a user context. */ if (ecb->dte_cond & DTRACE_COND_OWNER) { cred_t *cr; cred_t *s_cr = state->dts_cred.dcr_cred; proc_t *proc; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_uid != cr->cr_uid || s_cr->cr_uid != cr->cr_ruid || s_cr->cr_uid != cr->cr_suid || s_cr->cr_gid != cr->cr_gid || s_cr->cr_gid != cr->cr_rgid || s_cr->cr_gid != cr->cr_sgid || (proc = ttoproc(curthread)) == NULL || (proc->p_flag & SNOCD)) { if (mode & DTRACE_MODE_NOPRIV_DROP) return (0); #ifdef illumos mstate->dtms_access &= ~DTRACE_ACCESS_PROC; #endif } } #ifdef illumos /* * If our dte_cond is set to DTRACE_COND_ZONEOWNER and we are not * in our zone, check to see if our mode policy is to restrict rather * than to drop; if to restrict, strip away both DTRACE_ACCESS_PROC * and DTRACE_ACCESS_ARGS */ if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) { cred_t *cr; cred_t *s_cr = state->dts_cred.dcr_cred; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_zone->zone_id != cr->cr_zone->zone_id) { if (mode & DTRACE_MODE_NOPRIV_DROP) return (0); mstate->dtms_access &= ~(DTRACE_ACCESS_PROC | DTRACE_ACCESS_ARGS); } } #endif return (1); } /* * Note: not called from probe context. This function is called * asynchronously (and at a regular interval) from outside of probe context to * clean the dirty dynamic variable lists on all CPUs. Dynamic variable * cleaning is explained in detail in . */ void dtrace_dynvar_clean(dtrace_dstate_t *dstate) { dtrace_dynvar_t *dirty; dtrace_dstate_percpu_t *dcpu; dtrace_dynvar_t **rinsep; int i, j, work = 0; for (i = 0; i < NCPU; i++) { dcpu = &dstate->dtds_percpu[i]; rinsep = &dcpu->dtdsc_rinsing; /* * If the dirty list is NULL, there is no dirty work to do. */ if (dcpu->dtdsc_dirty == NULL) continue; if (dcpu->dtdsc_rinsing != NULL) { /* * If the rinsing list is non-NULL, then it is because * this CPU was selected to accept another CPU's * dirty list -- and since that time, dirty buffers * have accumulated. This is a highly unlikely * condition, but we choose to ignore the dirty * buffers -- they'll be picked up a future cleanse. */ continue; } if (dcpu->dtdsc_clean != NULL) { /* * If the clean list is non-NULL, then we're in a * situation where a CPU has done deallocations (we * have a non-NULL dirty list) but no allocations (we * also have a non-NULL clean list). We can't simply * move the dirty list into the clean list on this * CPU, yet we also don't want to allow this condition * to persist, lest a short clean list prevent a * massive dirty list from being cleaned (which in * turn could lead to otherwise avoidable dynamic * drops). To deal with this, we look for some CPU * with a NULL clean list, NULL dirty list, and NULL * rinsing list -- and then we borrow this CPU to * rinse our dirty list. */ for (j = 0; j < NCPU; j++) { dtrace_dstate_percpu_t *rinser; rinser = &dstate->dtds_percpu[j]; if (rinser->dtdsc_rinsing != NULL) continue; if (rinser->dtdsc_dirty != NULL) continue; if (rinser->dtdsc_clean != NULL) continue; rinsep = &rinser->dtdsc_rinsing; break; } if (j == NCPU) { /* * We were unable to find another CPU that * could accept this dirty list -- we are * therefore unable to clean it now. */ dtrace_dynvar_failclean++; continue; } } work = 1; /* * Atomically move the dirty list aside. */ do { dirty = dcpu->dtdsc_dirty; /* * Before we zap the dirty list, set the rinsing list. * (This allows for a potential assertion in * dtrace_dynvar(): if a free dynamic variable appears * on a hash chain, either the dirty list or the * rinsing list for some CPU must be non-NULL.) */ *rinsep = dirty; dtrace_membar_producer(); } while (dtrace_casptr(&dcpu->dtdsc_dirty, dirty, NULL) != dirty); } if (!work) { /* * We have no work to do; we can simply return. */ return; } dtrace_sync(); for (i = 0; i < NCPU; i++) { dcpu = &dstate->dtds_percpu[i]; if (dcpu->dtdsc_rinsing == NULL) continue; /* * We are now guaranteed that no hash chain contains a pointer * into this dirty list; we can make it clean. */ ASSERT(dcpu->dtdsc_clean == NULL); dcpu->dtdsc_clean = dcpu->dtdsc_rinsing; dcpu->dtdsc_rinsing = NULL; } /* * Before we actually set the state to be DTRACE_DSTATE_CLEAN, make * sure that all CPUs have seen all of the dtdsc_clean pointers. * This prevents a race whereby a CPU incorrectly decides that * the state should be something other than DTRACE_DSTATE_CLEAN * after dtrace_dynvar_clean() has completed. */ dtrace_sync(); dstate->dtds_state = DTRACE_DSTATE_CLEAN; } /* * Depending on the value of the op parameter, this function looks-up, * allocates or deallocates an arbitrarily-keyed dynamic variable. If an * allocation is requested, this function will return a pointer to a * dtrace_dynvar_t corresponding to the allocated variable -- or NULL if no * variable can be allocated. If NULL is returned, the appropriate counter * will be incremented. */ dtrace_dynvar_t * dtrace_dynvar(dtrace_dstate_t *dstate, uint_t nkeys, dtrace_key_t *key, size_t dsize, dtrace_dynvar_op_t op, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate) { uint64_t hashval = DTRACE_DYNHASH_VALID; dtrace_dynhash_t *hash = dstate->dtds_hash; dtrace_dynvar_t *free, *new_free, *next, *dvar, *start, *prev = NULL; processorid_t me = curcpu, cpu = me; dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[me]; size_t bucket, ksize; size_t chunksize = dstate->dtds_chunksize; uintptr_t kdata, lock, nstate; uint_t i; ASSERT(nkeys != 0); /* * Hash the key. As with aggregations, we use Jenkins' "One-at-a-time" * algorithm. For the by-value portions, we perform the algorithm in * 16-bit chunks (as opposed to 8-bit chunks). This speeds things up a * bit, and seems to have only a minute effect on distribution. For * the by-reference data, we perform "One-at-a-time" iterating (safely) * over each referenced byte. It's painful to do this, but it's much * better than pathological hash distribution. The efficacy of the * hashing algorithm (and a comparison with other algorithms) may be * found by running the ::dtrace_dynstat MDB dcmd. */ for (i = 0; i < nkeys; i++) { if (key[i].dttk_size == 0) { uint64_t val = key[i].dttk_value; hashval += (val >> 48) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += (val >> 32) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += (val >> 16) & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); hashval += val & 0xffff; hashval += (hashval << 10); hashval ^= (hashval >> 6); } else { /* * This is incredibly painful, but it beats the hell * out of the alternative. */ uint64_t j, size = key[i].dttk_size; uintptr_t base = (uintptr_t)key[i].dttk_value; if (!dtrace_canload(base, size, mstate, vstate)) break; for (j = 0; j < size; j++) { hashval += dtrace_load8(base + j); hashval += (hashval << 10); hashval ^= (hashval >> 6); } } } if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) return (NULL); hashval += (hashval << 3); hashval ^= (hashval >> 11); hashval += (hashval << 15); /* * There is a remote chance (ideally, 1 in 2^31) that our hashval * comes out to be one of our two sentinel hash values. If this * actually happens, we set the hashval to be a value known to be a * non-sentinel value. */ if (hashval == DTRACE_DYNHASH_FREE || hashval == DTRACE_DYNHASH_SINK) hashval = DTRACE_DYNHASH_VALID; /* * Yes, it's painful to do a divide here. If the cycle count becomes * important here, tricks can be pulled to reduce it. (However, it's * critical that hash collisions be kept to an absolute minimum; * they're much more painful than a divide.) It's better to have a * solution that generates few collisions and still keeps things * relatively simple. */ bucket = hashval % dstate->dtds_hashsize; if (op == DTRACE_DYNVAR_DEALLOC) { volatile uintptr_t *lockp = &hash[bucket].dtdh_lock; for (;;) { while ((lock = *lockp) & 1) continue; if (dtrace_casptr((volatile void *)lockp, (volatile void *)lock, (volatile void *)(lock + 1)) == (void *)lock) break; } dtrace_membar_producer(); } top: prev = NULL; lock = hash[bucket].dtdh_lock; dtrace_membar_consumer(); start = hash[bucket].dtdh_chain; ASSERT(start != NULL && (start->dtdv_hashval == DTRACE_DYNHASH_SINK || start->dtdv_hashval != DTRACE_DYNHASH_FREE || op != DTRACE_DYNVAR_DEALLOC)); for (dvar = start; dvar != NULL; dvar = dvar->dtdv_next) { dtrace_tuple_t *dtuple = &dvar->dtdv_tuple; dtrace_key_t *dkey = &dtuple->dtt_key[0]; if (dvar->dtdv_hashval != hashval) { if (dvar->dtdv_hashval == DTRACE_DYNHASH_SINK) { /* * We've reached the sink, and therefore the * end of the hash chain; we can kick out of * the loop knowing that we have seen a valid * snapshot of state. */ ASSERT(dvar->dtdv_next == NULL); ASSERT(dvar == &dtrace_dynhash_sink); break; } if (dvar->dtdv_hashval == DTRACE_DYNHASH_FREE) { /* * We've gone off the rails: somewhere along * the line, one of the members of this hash * chain was deleted. Note that we could also * detect this by simply letting this loop run * to completion, as we would eventually hit * the end of the dirty list. However, we * want to avoid running the length of the * dirty list unnecessarily (it might be quite * long), so we catch this as early as * possible by detecting the hash marker. In * this case, we simply set dvar to NULL and * break; the conditional after the loop will * send us back to top. */ dvar = NULL; break; } goto next; } if (dtuple->dtt_nkeys != nkeys) goto next; for (i = 0; i < nkeys; i++, dkey++) { if (dkey->dttk_size != key[i].dttk_size) goto next; /* size or type mismatch */ if (dkey->dttk_size != 0) { if (dtrace_bcmp( (void *)(uintptr_t)key[i].dttk_value, (void *)(uintptr_t)dkey->dttk_value, dkey->dttk_size)) goto next; } else { if (dkey->dttk_value != key[i].dttk_value) goto next; } } if (op != DTRACE_DYNVAR_DEALLOC) return (dvar); ASSERT(dvar->dtdv_next == NULL || dvar->dtdv_next->dtdv_hashval != DTRACE_DYNHASH_FREE); if (prev != NULL) { ASSERT(hash[bucket].dtdh_chain != dvar); ASSERT(start != dvar); ASSERT(prev->dtdv_next == dvar); prev->dtdv_next = dvar->dtdv_next; } else { if (dtrace_casptr(&hash[bucket].dtdh_chain, start, dvar->dtdv_next) != start) { /* * We have failed to atomically swing the * hash table head pointer, presumably because * of a conflicting allocation on another CPU. * We need to reread the hash chain and try * again. */ goto top; } } dtrace_membar_producer(); /* * Now set the hash value to indicate that it's free. */ ASSERT(hash[bucket].dtdh_chain != dvar); dvar->dtdv_hashval = DTRACE_DYNHASH_FREE; dtrace_membar_producer(); /* * Set the next pointer to point at the dirty list, and * atomically swing the dirty pointer to the newly freed dvar. */ do { next = dcpu->dtdsc_dirty; dvar->dtdv_next = next; } while (dtrace_casptr(&dcpu->dtdsc_dirty, next, dvar) != next); /* * Finally, unlock this hash bucket. */ ASSERT(hash[bucket].dtdh_lock == lock); ASSERT(lock & 1); hash[bucket].dtdh_lock++; return (NULL); next: prev = dvar; continue; } if (dvar == NULL) { /* * If dvar is NULL, it is because we went off the rails: * one of the elements that we traversed in the hash chain * was deleted while we were traversing it. In this case, * we assert that we aren't doing a dealloc (deallocs lock * the hash bucket to prevent themselves from racing with * one another), and retry the hash chain traversal. */ ASSERT(op != DTRACE_DYNVAR_DEALLOC); goto top; } if (op != DTRACE_DYNVAR_ALLOC) { /* * If we are not to allocate a new variable, we want to * return NULL now. Before we return, check that the value * of the lock word hasn't changed. If it has, we may have * seen an inconsistent snapshot. */ if (op == DTRACE_DYNVAR_NOALLOC) { if (hash[bucket].dtdh_lock != lock) goto top; } else { ASSERT(op == DTRACE_DYNVAR_DEALLOC); ASSERT(hash[bucket].dtdh_lock == lock); ASSERT(lock & 1); hash[bucket].dtdh_lock++; } return (NULL); } /* * We need to allocate a new dynamic variable. The size we need is the * size of dtrace_dynvar plus the size of nkeys dtrace_key_t's plus the * size of any auxiliary key data (rounded up to 8-byte alignment) plus * the size of any referred-to data (dsize). We then round the final * size up to the chunksize for allocation. */ for (ksize = 0, i = 0; i < nkeys; i++) ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t)); /* * This should be pretty much impossible, but could happen if, say, * strange DIF specified the tuple. Ideally, this should be an * assertion and not an error condition -- but that requires that the * chunksize calculation in dtrace_difo_chunksize() be absolutely * bullet-proof. (That is, it must not be able to be fooled by * malicious DIF.) Given the lack of backwards branches in DIF, * solving this would presumably not amount to solving the Halting * Problem -- but it still seems awfully hard. */ if (sizeof (dtrace_dynvar_t) + sizeof (dtrace_key_t) * (nkeys - 1) + ksize + dsize > chunksize) { dcpu->dtdsc_drops++; return (NULL); } nstate = DTRACE_DSTATE_EMPTY; do { retry: free = dcpu->dtdsc_free; if (free == NULL) { dtrace_dynvar_t *clean = dcpu->dtdsc_clean; void *rval; if (clean == NULL) { /* * We're out of dynamic variable space on * this CPU. Unless we have tried all CPUs, * we'll try to allocate from a different * CPU. */ switch (dstate->dtds_state) { case DTRACE_DSTATE_CLEAN: { void *sp = &dstate->dtds_state; if (++cpu >= NCPU) cpu = 0; if (dcpu->dtdsc_dirty != NULL && nstate == DTRACE_DSTATE_EMPTY) nstate = DTRACE_DSTATE_DIRTY; if (dcpu->dtdsc_rinsing != NULL) nstate = DTRACE_DSTATE_RINSING; dcpu = &dstate->dtds_percpu[cpu]; if (cpu != me) goto retry; (void) dtrace_cas32(sp, DTRACE_DSTATE_CLEAN, nstate); /* * To increment the correct bean * counter, take another lap. */ goto retry; } case DTRACE_DSTATE_DIRTY: dcpu->dtdsc_dirty_drops++; break; case DTRACE_DSTATE_RINSING: dcpu->dtdsc_rinsing_drops++; break; case DTRACE_DSTATE_EMPTY: dcpu->dtdsc_drops++; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_DROP); return (NULL); } /* * The clean list appears to be non-empty. We want to * move the clean list to the free list; we start by * moving the clean pointer aside. */ if (dtrace_casptr(&dcpu->dtdsc_clean, clean, NULL) != clean) { /* * We are in one of two situations: * * (a) The clean list was switched to the * free list by another CPU. * * (b) The clean list was added to by the * cleansing cyclic. * * In either of these situations, we can * just reattempt the free list allocation. */ goto retry; } ASSERT(clean->dtdv_hashval == DTRACE_DYNHASH_FREE); /* * Now we'll move the clean list to our free list. * It's impossible for this to fail: the only way * the free list can be updated is through this * code path, and only one CPU can own the clean list. * Thus, it would only be possible for this to fail if * this code were racing with dtrace_dynvar_clean(). * (That is, if dtrace_dynvar_clean() updated the clean * list, and we ended up racing to update the free * list.) This race is prevented by the dtrace_sync() * in dtrace_dynvar_clean() -- which flushes the * owners of the clean lists out before resetting * the clean lists. */ dcpu = &dstate->dtds_percpu[me]; rval = dtrace_casptr(&dcpu->dtdsc_free, NULL, clean); ASSERT(rval == NULL); goto retry; } dvar = free; new_free = dvar->dtdv_next; } while (dtrace_casptr(&dcpu->dtdsc_free, free, new_free) != free); /* * We have now allocated a new chunk. We copy the tuple keys into the * tuple array and copy any referenced key data into the data space * following the tuple array. As we do this, we relocate dttk_value * in the final tuple to point to the key data address in the chunk. */ kdata = (uintptr_t)&dvar->dtdv_tuple.dtt_key[nkeys]; dvar->dtdv_data = (void *)(kdata + ksize); dvar->dtdv_tuple.dtt_nkeys = nkeys; for (i = 0; i < nkeys; i++) { dtrace_key_t *dkey = &dvar->dtdv_tuple.dtt_key[i]; size_t kesize = key[i].dttk_size; if (kesize != 0) { dtrace_bcopy( (const void *)(uintptr_t)key[i].dttk_value, (void *)kdata, kesize); dkey->dttk_value = kdata; kdata += P2ROUNDUP(kesize, sizeof (uint64_t)); } else { dkey->dttk_value = key[i].dttk_value; } dkey->dttk_size = kesize; } ASSERT(dvar->dtdv_hashval == DTRACE_DYNHASH_FREE); dvar->dtdv_hashval = hashval; dvar->dtdv_next = start; if (dtrace_casptr(&hash[bucket].dtdh_chain, start, dvar) == start) return (dvar); /* * The cas has failed. Either another CPU is adding an element to * this hash chain, or another CPU is deleting an element from this * hash chain. The simplest way to deal with both of these cases * (though not necessarily the most efficient) is to free our * allocated block and tail-call ourselves. Note that the free is * to the dirty list and _not_ to the free list. This is to prevent * races with allocators, above. */ dvar->dtdv_hashval = DTRACE_DYNHASH_FREE; dtrace_membar_producer(); do { free = dcpu->dtdsc_dirty; dvar->dtdv_next = free; } while (dtrace_casptr(&dcpu->dtdsc_dirty, free, dvar) != free); return (dtrace_dynvar(dstate, nkeys, key, dsize, op, mstate, vstate)); } /*ARGSUSED*/ static void dtrace_aggregate_min(uint64_t *oval, uint64_t nval, uint64_t arg) { if ((int64_t)nval < (int64_t)*oval) *oval = nval; } /*ARGSUSED*/ static void dtrace_aggregate_max(uint64_t *oval, uint64_t nval, uint64_t arg) { if ((int64_t)nval > (int64_t)*oval) *oval = nval; } static void dtrace_aggregate_quantize(uint64_t *quanta, uint64_t nval, uint64_t incr) { int i, zero = DTRACE_QUANTIZE_ZEROBUCKET; int64_t val = (int64_t)nval; if (val < 0) { for (i = 0; i < zero; i++) { if (val <= DTRACE_QUANTIZE_BUCKETVAL(i)) { quanta[i] += incr; return; } } } else { for (i = zero + 1; i < DTRACE_QUANTIZE_NBUCKETS; i++) { if (val < DTRACE_QUANTIZE_BUCKETVAL(i)) { quanta[i - 1] += incr; return; } } quanta[DTRACE_QUANTIZE_NBUCKETS - 1] += incr; return; } ASSERT(0); } static void dtrace_aggregate_lquantize(uint64_t *lquanta, uint64_t nval, uint64_t incr) { uint64_t arg = *lquanta++; int32_t base = DTRACE_LQUANTIZE_BASE(arg); uint16_t step = DTRACE_LQUANTIZE_STEP(arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg); int32_t val = (int32_t)nval, level; ASSERT(step != 0); ASSERT(levels != 0); if (val < base) { /* * This is an underflow. */ lquanta[0] += incr; return; } level = (val - base) / step; if (level < levels) { lquanta[level + 1] += incr; return; } /* * This is an overflow. */ lquanta[levels + 1] += incr; } static int dtrace_aggregate_llquantize_bucket(uint16_t factor, uint16_t low, uint16_t high, uint16_t nsteps, int64_t value) { int64_t this = 1, last, next; int base = 1, order; ASSERT(factor <= nsteps); ASSERT(nsteps % factor == 0); for (order = 0; order < low; order++) this *= factor; /* * If our value is less than our factor taken to the power of the * low order of magnitude, it goes into the zeroth bucket. */ if (value < (last = this)) return (0); for (this *= factor; order <= high; order++) { int nbuckets = this > nsteps ? nsteps : this; if ((next = this * factor) < this) { /* * We should not generally get log/linear quantizations * with a high magnitude that allows 64-bits to * overflow, but we nonetheless protect against this * by explicitly checking for overflow, and clamping * our value accordingly. */ value = this - 1; } if (value < this) { /* * If our value lies within this order of magnitude, * determine its position by taking the offset within * the order of magnitude, dividing by the bucket * width, and adding to our (accumulated) base. */ return (base + (value - last) / (this / nbuckets)); } base += nbuckets - (nbuckets / factor); last = this; this = next; } /* * Our value is greater than or equal to our factor taken to the * power of one plus the high magnitude -- return the top bucket. */ return (base); } static void dtrace_aggregate_llquantize(uint64_t *llquanta, uint64_t nval, uint64_t incr) { uint64_t arg = *llquanta++; uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); llquanta[dtrace_aggregate_llquantize_bucket(factor, low, high, nsteps, nval)] += incr; } /*ARGSUSED*/ static void dtrace_aggregate_avg(uint64_t *data, uint64_t nval, uint64_t arg) { data[0]++; data[1] += nval; } /*ARGSUSED*/ static void dtrace_aggregate_stddev(uint64_t *data, uint64_t nval, uint64_t arg) { int64_t snval = (int64_t)nval; uint64_t tmp[2]; data[0]++; data[1] += nval; /* * What we want to say here is: * * data[2] += nval * nval; * * But given that nval is 64-bit, we could easily overflow, so * we do this as 128-bit arithmetic. */ if (snval < 0) snval = -snval; dtrace_multiply_128((uint64_t)snval, (uint64_t)snval, tmp); dtrace_add_128(data + 2, tmp, data + 2); } /*ARGSUSED*/ static void dtrace_aggregate_count(uint64_t *oval, uint64_t nval, uint64_t arg) { *oval = *oval + 1; } /*ARGSUSED*/ static void dtrace_aggregate_sum(uint64_t *oval, uint64_t nval, uint64_t arg) { *oval += nval; } /* * Aggregate given the tuple in the principal data buffer, and the aggregating * action denoted by the specified dtrace_aggregation_t. The aggregation * buffer is specified as the buf parameter. This routine does not return * failure; if there is no space in the aggregation buffer, the data will be * dropped, and a corresponding counter incremented. */ static void dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, intptr_t offset, dtrace_buffer_t *buf, uint64_t expr, uint64_t arg) { dtrace_recdesc_t *rec = &agg->dtag_action.dta_rec; uint32_t i, ndx, size, fsize; uint32_t align = sizeof (uint64_t) - 1; dtrace_aggbuffer_t *agb; dtrace_aggkey_t *key; uint32_t hashval = 0, limit, isstr; caddr_t tomax, data, kdata; dtrace_actkind_t action; dtrace_action_t *act; uintptr_t offs; if (buf == NULL) return; if (!agg->dtag_hasarg) { /* * Currently, only quantize() and lquantize() take additional * arguments, and they have the same semantics: an increment * value that defaults to 1 when not present. If additional * aggregating actions take arguments, the setting of the * default argument value will presumably have to become more * sophisticated... */ arg = 1; } action = agg->dtag_action.dta_kind - DTRACEACT_AGGREGATION; size = rec->dtrd_offset - agg->dtag_base; fsize = size + rec->dtrd_size; ASSERT(dbuf->dtb_tomax != NULL); data = dbuf->dtb_tomax + offset + agg->dtag_base; if ((tomax = buf->dtb_tomax) == NULL) { dtrace_buffer_drop(buf); return; } /* * The metastructure is always at the bottom of the buffer. */ agb = (dtrace_aggbuffer_t *)(tomax + buf->dtb_size - sizeof (dtrace_aggbuffer_t)); if (buf->dtb_offset == 0) { /* * We just kludge up approximately 1/8th of the size to be * buckets. If this guess ends up being routinely * off-the-mark, we may need to dynamically readjust this * based on past performance. */ uintptr_t hashsize = (buf->dtb_size >> 3) / sizeof (uintptr_t); if ((uintptr_t)agb - hashsize * sizeof (dtrace_aggkey_t *) < (uintptr_t)tomax || hashsize == 0) { /* * We've been given a ludicrously small buffer; * increment our drop count and leave. */ dtrace_buffer_drop(buf); return; } /* * And now, a pathetic attempt to try to get a an odd (or * perchance, a prime) hash size for better hash distribution. */ if (hashsize > (DTRACE_AGGHASHSIZE_SLEW << 3)) hashsize -= DTRACE_AGGHASHSIZE_SLEW; agb->dtagb_hashsize = hashsize; agb->dtagb_hash = (dtrace_aggkey_t **)((uintptr_t)agb - agb->dtagb_hashsize * sizeof (dtrace_aggkey_t *)); agb->dtagb_free = (uintptr_t)agb->dtagb_hash; for (i = 0; i < agb->dtagb_hashsize; i++) agb->dtagb_hash[i] = NULL; } ASSERT(agg->dtag_first != NULL); ASSERT(agg->dtag_first->dta_intuple); /* * Calculate the hash value based on the key. Note that we _don't_ * include the aggid in the hashing (but we will store it as part of * the key). The hashing algorithm is Bob Jenkins' "One-at-a-time" * algorithm: a simple, quick algorithm that has no known funnels, and * gets good distribution in practice. The efficacy of the hashing * algorithm (and a comparison with other algorithms) may be found by * running the ::dtrace_aggstat MDB dcmd. */ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); isstr = DTRACEACT_ISSTRING(act); for (; i < limit; i++) { hashval += data[i]; hashval += (hashval << 10); hashval ^= (hashval >> 6); if (isstr && data[i] == '\0') break; } } hashval += (hashval << 3); hashval ^= (hashval >> 11); hashval += (hashval << 15); /* * Yes, the divide here is expensive -- but it's generally the least * of the performance issues given the amount of data that we iterate * over to compute hash values, compare data, etc. */ ndx = hashval % agb->dtagb_hashsize; for (key = agb->dtagb_hash[ndx]; key != NULL; key = key->dtak_next) { ASSERT((caddr_t)key >= tomax); ASSERT((caddr_t)key < tomax + buf->dtb_size); if (hashval != key->dtak_hashval || key->dtak_size != size) continue; kdata = key->dtak_data; ASSERT(kdata >= tomax && kdata < tomax + buf->dtb_size); for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); isstr = DTRACEACT_ISSTRING(act); for (; i < limit; i++) { if (kdata[i] != data[i]) goto next; if (isstr && data[i] == '\0') break; } } if (action != key->dtak_action) { /* * We are aggregating on the same value in the same * aggregation with two different aggregating actions. * (This should have been picked up in the compiler, * so we may be dealing with errant or devious DIF.) * This is an error condition; we indicate as much, * and return. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return; } /* * This is a hit: we need to apply the aggregator to * the value at this key. */ agg->dtag_aggregate((uint64_t *)(kdata + size), expr, arg); return; next: continue; } /* * We didn't find it. We need to allocate some zero-filled space, * link it into the hash table appropriately, and apply the aggregator * to the (zero-filled) value. */ offs = buf->dtb_offset; while (offs & (align - 1)) offs += sizeof (uint32_t); /* * If we don't have enough room to both allocate a new key _and_ * its associated data, increment the drop count and return. */ if ((uintptr_t)tomax + offs + fsize > agb->dtagb_free - sizeof (dtrace_aggkey_t)) { dtrace_buffer_drop(buf); return; } /*CONSTCOND*/ ASSERT(!(sizeof (dtrace_aggkey_t) & (sizeof (uintptr_t) - 1))); key = (dtrace_aggkey_t *)(agb->dtagb_free - sizeof (dtrace_aggkey_t)); agb->dtagb_free -= sizeof (dtrace_aggkey_t); key->dtak_data = kdata = tomax + offs; buf->dtb_offset = offs + fsize; /* * Now copy the data across. */ *((dtrace_aggid_t *)kdata) = agg->dtag_id; for (i = sizeof (dtrace_aggid_t); i < size; i++) kdata[i] = data[i]; /* * Because strings are not zeroed out by default, we need to iterate * looking for actions that store strings, and we need to explicitly * pad these strings out with zeroes. */ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { int nul; if (!DTRACEACT_ISSTRING(act)) continue; i = act->dta_rec.dtrd_offset - agg->dtag_base; limit = i + act->dta_rec.dtrd_size; ASSERT(limit <= size); for (nul = 0; i < limit; i++) { if (nul) { kdata[i] = '\0'; continue; } if (data[i] != '\0') continue; nul = 1; } } for (i = size; i < fsize; i++) kdata[i] = 0; key->dtak_hashval = hashval; key->dtak_size = size; key->dtak_action = action; key->dtak_next = agb->dtagb_hash[ndx]; agb->dtagb_hash[ndx] = key; /* * Finally, apply the aggregator. */ *((uint64_t *)(key->dtak_data + size)) = agg->dtag_initial; agg->dtag_aggregate((uint64_t *)(key->dtak_data + size), expr, arg); } /* * Given consumer state, this routine finds a speculation in the INACTIVE * state and transitions it into the ACTIVE state. If there is no speculation * in the INACTIVE state, 0 is returned. In this case, no error counter is * incremented -- it is up to the caller to take appropriate action. */ static int dtrace_speculation(dtrace_state_t *state) { int i = 0; dtrace_speculation_state_t current; uint32_t *stat = &state->dts_speculations_unavail, count; while (i < state->dts_nspeculations) { dtrace_speculation_t *spec = &state->dts_speculations[i]; current = spec->dtsp_state; if (current != DTRACESPEC_INACTIVE) { if (current == DTRACESPEC_COMMITTINGMANY || current == DTRACESPEC_COMMITTING || current == DTRACESPEC_DISCARDING) stat = &state->dts_speculations_busy; i++; continue; } if (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, DTRACESPEC_ACTIVE) == current) return (i + 1); } /* * We couldn't find a speculation. If we found as much as a single * busy speculation buffer, we'll attribute this failure as "busy" * instead of "unavail". */ do { count = *stat; } while (dtrace_cas32(stat, count, count + 1) != count); return (0); } /* * This routine commits an active speculation. If the specified speculation * is not in a valid state to perform a commit(), this routine will silently do * nothing. The state of the specified speculation is transitioned according * to the state transition diagram outlined in */ static void dtrace_speculation_commit(dtrace_state_t *state, processorid_t cpu, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_buffer_t *src, *dest; uintptr_t daddr, saddr, dlimit, slimit; dtrace_speculation_state_t current, new = 0; intptr_t offs; uint64_t timestamp; if (which == 0) return; if (which > state->dts_nspeculations) { cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return; } spec = &state->dts_speculations[which - 1]; src = &spec->dtsp_buffer[cpu]; dest = &state->dts_buffer[cpu]; do { current = spec->dtsp_state; if (current == DTRACESPEC_COMMITTINGMANY) break; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_DISCARDING: return; case DTRACESPEC_COMMITTING: /* * This is only possible if we are (a) commit()'ing * without having done a prior speculate() on this CPU * and (b) racing with another commit() on a different * CPU. There's nothing to do -- we just assert that * our offset is 0. */ ASSERT(src->dtb_offset == 0); return; case DTRACESPEC_ACTIVE: new = DTRACESPEC_COMMITTING; break; case DTRACESPEC_ACTIVEONE: /* * This speculation is active on one CPU. If our * buffer offset is non-zero, we know that the one CPU * must be us. Otherwise, we are committing on a * different CPU from the speculate(), and we must * rely on being asynchronously cleaned. */ if (src->dtb_offset != 0) { new = DTRACESPEC_COMMITTING; break; } /*FALLTHROUGH*/ case DTRACESPEC_ACTIVEMANY: new = DTRACESPEC_COMMITTINGMANY; break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); /* * We have set the state to indicate that we are committing this * speculation. Now reserve the necessary space in the destination * buffer. */ if ((offs = dtrace_buffer_reserve(dest, src->dtb_offset, sizeof (uint64_t), state, NULL)) < 0) { dtrace_buffer_drop(dest); goto out; } /* * We have sufficient space to copy the speculative buffer into the * primary buffer. First, modify the speculative buffer, filling * in the timestamp of all entries with the current time. The data * must have the commit() time rather than the time it was traced, * so that all entries in the primary buffer are in timestamp order. */ timestamp = dtrace_gethrtime(); saddr = (uintptr_t)src->dtb_tomax; slimit = saddr + src->dtb_offset; while (saddr < slimit) { size_t size; dtrace_rechdr_t *dtrh = (dtrace_rechdr_t *)saddr; if (dtrh->dtrh_epid == DTRACE_EPIDNONE) { saddr += sizeof (dtrace_epid_t); continue; } ASSERT3U(dtrh->dtrh_epid, <=, state->dts_necbs); size = state->dts_ecbs[dtrh->dtrh_epid - 1]->dte_size; ASSERT3U(saddr + size, <=, slimit); ASSERT3U(size, >=, sizeof (dtrace_rechdr_t)); ASSERT3U(DTRACE_RECORD_LOAD_TIMESTAMP(dtrh), ==, UINT64_MAX); DTRACE_RECORD_STORE_TIMESTAMP(dtrh, timestamp); saddr += size; } /* * Copy the buffer across. (Note that this is a * highly subobtimal bcopy(); in the unlikely event that this becomes * a serious performance issue, a high-performance DTrace-specific * bcopy() should obviously be invented.) */ daddr = (uintptr_t)dest->dtb_tomax + offs; dlimit = daddr + src->dtb_offset; saddr = (uintptr_t)src->dtb_tomax; /* * First, the aligned portion. */ while (dlimit - daddr >= sizeof (uint64_t)) { *((uint64_t *)daddr) = *((uint64_t *)saddr); daddr += sizeof (uint64_t); saddr += sizeof (uint64_t); } /* * Now any left-over bit... */ while (dlimit - daddr) *((uint8_t *)daddr++) = *((uint8_t *)saddr++); /* * Finally, commit the reserved space in the destination buffer. */ dest->dtb_offset = offs + src->dtb_offset; out: /* * If we're lucky enough to be the only active CPU on this speculation * buffer, we can just set the state back to DTRACESPEC_INACTIVE. */ if (current == DTRACESPEC_ACTIVE || (current == DTRACESPEC_ACTIVEONE && new == DTRACESPEC_COMMITTING)) { uint32_t rval = dtrace_cas32((uint32_t *)&spec->dtsp_state, DTRACESPEC_COMMITTING, DTRACESPEC_INACTIVE); ASSERT(rval == DTRACESPEC_COMMITTING); } src->dtb_offset = 0; src->dtb_xamot_drops += src->dtb_drops; src->dtb_drops = 0; } /* * This routine discards an active speculation. If the specified speculation * is not in a valid state to perform a discard(), this routine will silently * do nothing. The state of the specified speculation is transitioned * according to the state transition diagram outlined in */ static void dtrace_speculation_discard(dtrace_state_t *state, processorid_t cpu, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_speculation_state_t current, new = 0; dtrace_buffer_t *buf; if (which == 0) return; if (which > state->dts_nspeculations) { cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return; } spec = &state->dts_speculations[which - 1]; buf = &spec->dtsp_buffer[cpu]; do { current = spec->dtsp_state; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_COMMITTINGMANY: case DTRACESPEC_COMMITTING: case DTRACESPEC_DISCARDING: return; case DTRACESPEC_ACTIVE: case DTRACESPEC_ACTIVEMANY: new = DTRACESPEC_DISCARDING; break; case DTRACESPEC_ACTIVEONE: if (buf->dtb_offset != 0) { new = DTRACESPEC_INACTIVE; } else { new = DTRACESPEC_DISCARDING; } break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); buf->dtb_offset = 0; buf->dtb_drops = 0; } /* * Note: not called from probe context. This function is called * asynchronously from cross call context to clean any speculations that are * in the COMMITTINGMANY or DISCARDING states. These speculations may not be * transitioned back to the INACTIVE state until all CPUs have cleaned the * speculation. */ static void dtrace_speculation_clean_here(dtrace_state_t *state) { dtrace_icookie_t cookie; processorid_t cpu = curcpu; dtrace_buffer_t *dest = &state->dts_buffer[cpu]; dtrace_specid_t i; cookie = dtrace_interrupt_disable(); if (dest->dtb_tomax == NULL) { dtrace_interrupt_enable(cookie); return; } for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; dtrace_buffer_t *src = &spec->dtsp_buffer[cpu]; if (src->dtb_tomax == NULL) continue; if (spec->dtsp_state == DTRACESPEC_DISCARDING) { src->dtb_offset = 0; continue; } if (spec->dtsp_state != DTRACESPEC_COMMITTINGMANY) continue; if (src->dtb_offset == 0) continue; dtrace_speculation_commit(state, cpu, i + 1); } dtrace_interrupt_enable(cookie); } /* * Note: not called from probe context. This function is called * asynchronously (and at a regular interval) to clean any speculations that * are in the COMMITTINGMANY or DISCARDING states. If it discovers that there * is work to be done, it cross calls all CPUs to perform that work; * COMMITMANY and DISCARDING speculations may not be transitioned back to the * INACTIVE state until they have been cleaned by all CPUs. */ static void dtrace_speculation_clean(dtrace_state_t *state) { int work = 0, rv; dtrace_specid_t i; for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; ASSERT(!spec->dtsp_cleaning); if (spec->dtsp_state != DTRACESPEC_DISCARDING && spec->dtsp_state != DTRACESPEC_COMMITTINGMANY) continue; work++; spec->dtsp_cleaning = 1; } if (!work) return; dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_speculation_clean_here, state); /* * We now know that all CPUs have committed or discarded their * speculation buffers, as appropriate. We can now set the state * to inactive. */ for (i = 0; i < state->dts_nspeculations; i++) { dtrace_speculation_t *spec = &state->dts_speculations[i]; dtrace_speculation_state_t current, new; if (!spec->dtsp_cleaning) continue; current = spec->dtsp_state; ASSERT(current == DTRACESPEC_DISCARDING || current == DTRACESPEC_COMMITTINGMANY); new = DTRACESPEC_INACTIVE; rv = dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new); ASSERT(rv == current); spec->dtsp_cleaning = 0; } } /* * Called as part of a speculate() to get the speculative buffer associated * with a given speculation. Returns NULL if the specified speculation is not * in an ACTIVE state. If the speculation is in the ACTIVEONE state -- and * the active CPU is not the specified CPU -- the speculation will be * atomically transitioned into the ACTIVEMANY state. */ static dtrace_buffer_t * dtrace_speculation_buffer(dtrace_state_t *state, processorid_t cpuid, dtrace_specid_t which) { dtrace_speculation_t *spec; dtrace_speculation_state_t current, new = 0; dtrace_buffer_t *buf; if (which == 0) return (NULL); if (which > state->dts_nspeculations) { cpu_core[cpuid].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP; return (NULL); } spec = &state->dts_speculations[which - 1]; buf = &spec->dtsp_buffer[cpuid]; do { current = spec->dtsp_state; switch (current) { case DTRACESPEC_INACTIVE: case DTRACESPEC_COMMITTINGMANY: case DTRACESPEC_DISCARDING: return (NULL); case DTRACESPEC_COMMITTING: ASSERT(buf->dtb_offset == 0); return (NULL); case DTRACESPEC_ACTIVEONE: /* * This speculation is currently active on one CPU. * Check the offset in the buffer; if it's non-zero, * that CPU must be us (and we leave the state alone). * If it's zero, assume that we're starting on a new * CPU -- and change the state to indicate that the * speculation is active on more than one CPU. */ if (buf->dtb_offset != 0) return (buf); new = DTRACESPEC_ACTIVEMANY; break; case DTRACESPEC_ACTIVEMANY: return (buf); case DTRACESPEC_ACTIVE: new = DTRACESPEC_ACTIVEONE; break; default: ASSERT(0); } } while (dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new) != current); ASSERT(new == DTRACESPEC_ACTIVEONE || new == DTRACESPEC_ACTIVEMANY); return (buf); } /* * Return a string. In the event that the user lacks the privilege to access * arbitrary kernel memory, we copy the string out to scratch memory so that we * don't fail access checking. * * dtrace_dif_variable() uses this routine as a helper for various * builtin values such as 'execname' and 'probefunc.' */ uintptr_t dtrace_dif_varstr(uintptr_t addr, dtrace_state_t *state, dtrace_mstate_t *mstate) { uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t ret; size_t strsz; /* * The easy case: this probe is allowed to read all of memory, so * we can just return this as a vanilla pointer. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0) return (addr); /* * This is the tougher case: we copy the string in question from * kernel memory into scratch memory and return it that way: this * ensures that we won't trip up when access checking tests the * BYREF return value. */ strsz = dtrace_strlen((char *)addr, size) + 1; if (mstate->dtms_scratch_ptr + strsz > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return (0); } dtrace_strcpy((const void *)addr, (void *)mstate->dtms_scratch_ptr, strsz); ret = mstate->dtms_scratch_ptr; mstate->dtms_scratch_ptr += strsz; return (ret); } /* * Return a string from a memoy address which is known to have one or * more concatenated, individually zero terminated, sub-strings. * In the event that the user lacks the privilege to access * arbitrary kernel memory, we copy the string out to scratch memory so that we * don't fail access checking. * * dtrace_dif_variable() uses this routine as a helper for various * builtin values such as 'execargs'. */ static uintptr_t dtrace_dif_varstrz(uintptr_t addr, size_t strsz, dtrace_state_t *state, dtrace_mstate_t *mstate) { char *p; size_t i; uintptr_t ret; if (mstate->dtms_scratch_ptr + strsz > mstate->dtms_scratch_base + mstate->dtms_scratch_size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return (0); } dtrace_bcopy((const void *)addr, (void *)mstate->dtms_scratch_ptr, strsz); /* Replace sub-string termination characters with a space. */ for (p = (char *) mstate->dtms_scratch_ptr, i = 0; i < strsz - 1; p++, i++) if (*p == '\0') *p = ' '; ret = mstate->dtms_scratch_ptr; mstate->dtms_scratch_ptr += strsz; return (ret); } /* * This function implements the DIF emulator's variable lookups. The emulator * passes a reserved variable identifier and optional built-in array index. */ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v, uint64_t ndx) { /* * If we're accessing one of the uncached arguments, we'll turn this * into a reference in the args array. */ if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9) { ndx = v - DIF_VAR_ARG0; v = DIF_VAR_ARGS; } switch (v) { case DIF_VAR_ARGS: ASSERT(mstate->dtms_present & DTRACE_MSTATE_ARGS); if (ndx >= sizeof (mstate->dtms_arg) / sizeof (mstate->dtms_arg[0])) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; dtrace_provider_t *pv; uint64_t val; pv = mstate->dtms_probe->dtpr_provider; if (pv->dtpv_pops.dtps_getargval != NULL) val = pv->dtpv_pops.dtps_getargval(pv->dtpv_arg, mstate->dtms_probe->dtpr_id, mstate->dtms_probe->dtpr_arg, ndx, aframes); else val = dtrace_getarg(ndx, aframes); /* * This is regrettably required to keep the compiler * from tail-optimizing the call to dtrace_getarg(). * The condition always evaluates to true, but the * compiler has no way of figuring that out a priori. * (None of this would be necessary if the compiler * could be relied upon to _always_ tail-optimize * the call to dtrace_getarg() -- but it can't.) */ if (mstate->dtms_probe != NULL) return (val); ASSERT(0); } return (mstate->dtms_arg[ndx]); #ifdef illumos case DIF_VAR_UREGS: { klwp_t *lwp; if (!dtrace_priv_proc(state)) return (0); if ((lwp = curthread->t_lwp) == NULL) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = NULL; return (0); } return (dtrace_getreg(lwp->lwp_regs, ndx)); return (0); } #else case DIF_VAR_UREGS: { struct trapframe *tframe; if (!dtrace_priv_proc(state)) return (0); if ((tframe = curthread->td_frame) == NULL) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = 0; return (0); } return (dtrace_getreg(tframe, ndx)); } #endif case DIF_VAR_CURTHREAD: if (!dtrace_priv_proc(state)) return (0); return ((uint64_t)(uintptr_t)curthread); case DIF_VAR_TIMESTAMP: if (!(mstate->dtms_present & DTRACE_MSTATE_TIMESTAMP)) { mstate->dtms_timestamp = dtrace_gethrtime(); mstate->dtms_present |= DTRACE_MSTATE_TIMESTAMP; } return (mstate->dtms_timestamp); case DIF_VAR_VTIMESTAMP: ASSERT(dtrace_vtime_references != 0); return (curthread->t_dtrace_vtime); case DIF_VAR_WALLTIMESTAMP: if (!(mstate->dtms_present & DTRACE_MSTATE_WALLTIMESTAMP)) { mstate->dtms_walltimestamp = dtrace_gethrestime(); mstate->dtms_present |= DTRACE_MSTATE_WALLTIMESTAMP; } return (mstate->dtms_walltimestamp); #ifdef illumos case DIF_VAR_IPL: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_IPL)) { mstate->dtms_ipl = dtrace_getipl(); mstate->dtms_present |= DTRACE_MSTATE_IPL; } return (mstate->dtms_ipl); #endif case DIF_VAR_EPID: ASSERT(mstate->dtms_present & DTRACE_MSTATE_EPID); return (mstate->dtms_epid); case DIF_VAR_ID: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (mstate->dtms_probe->dtpr_id); case DIF_VAR_STACKDEPTH: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_STACKDEPTH)) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; mstate->dtms_stackdepth = dtrace_getstackdepth(aframes); mstate->dtms_present |= DTRACE_MSTATE_STACKDEPTH; } return (mstate->dtms_stackdepth); case DIF_VAR_USTACKDEPTH: if (!dtrace_priv_proc(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_USTACKDEPTH)) { /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) { mstate->dtms_ustackdepth = 0; } else { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); mstate->dtms_ustackdepth = dtrace_getustackdepth(); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } mstate->dtms_present |= DTRACE_MSTATE_USTACKDEPTH; } return (mstate->dtms_ustackdepth); case DIF_VAR_CALLER: if (!dtrace_priv_kernel(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_CALLER)) { int aframes = mstate->dtms_probe->dtpr_aframes + 2; if (!DTRACE_ANCHORED(mstate->dtms_probe)) { /* * If this is an unanchored probe, we are * required to go through the slow path: * dtrace_caller() only guarantees correct * results for anchored probes. */ pc_t caller[2] = {0, 0}; dtrace_getpcstack(caller, 2, aframes, (uint32_t *)(uintptr_t)mstate->dtms_arg[0]); mstate->dtms_caller = caller[1]; } else if ((mstate->dtms_caller = dtrace_caller(aframes)) == -1) { /* * We have failed to do this the quick way; * we must resort to the slower approach of * calling dtrace_getpcstack(). */ pc_t caller = 0; dtrace_getpcstack(&caller, 1, aframes, NULL); mstate->dtms_caller = caller; } mstate->dtms_present |= DTRACE_MSTATE_CALLER; } return (mstate->dtms_caller); case DIF_VAR_UCALLER: if (!dtrace_priv_proc(state)) return (0); if (!(mstate->dtms_present & DTRACE_MSTATE_UCALLER)) { uint64_t ustack[3]; /* * dtrace_getupcstack() fills in the first uint64_t * with the current PID. The second uint64_t will * be the program counter at user-level. The third * uint64_t will contain the caller, which is what * we're after. */ ustack[2] = 0; DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getupcstack(ustack, 3); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); mstate->dtms_ucaller = ustack[2]; mstate->dtms_present |= DTRACE_MSTATE_UCALLER; } return (mstate->dtms_ucaller); case DIF_VAR_PROBEPROV: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_provider->dtpv_name, state, mstate)); case DIF_VAR_PROBEMOD: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_mod, state, mstate)); case DIF_VAR_PROBEFUNC: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_func, state, mstate)); case DIF_VAR_PROBENAME: ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE); return (dtrace_dif_varstr( (uintptr_t)mstate->dtms_probe->dtpr_name, state, mstate)); case DIF_VAR_PID: if (!dtrace_priv_proc(state)) return (0); #ifdef illumos /* * Note that we are assuming that an unanchored probe is * always due to a high-level interrupt. (And we're assuming * that there is only a single high level interrupt.) */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (pid0.pid_id); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * Further, it is always safe to dereference the p_pidp member * of one's own proc structure. (These are truisms becuase * threads and processes don't clean up their own state -- * they leave that task to whomever reaps them.) */ return ((uint64_t)curthread->t_procp->p_pidp->pid_id); #else return ((uint64_t)curproc->p_pid); #endif case DIF_VAR_PPID: if (!dtrace_priv_proc(state)) return (0); #ifdef illumos /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (pid0.pid_id); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return ((uint64_t)curthread->t_procp->p_ppid); #else if (curproc->p_pid == proc0.p_pid) return (curproc->p_pid); else return (curproc->p_pptr->p_pid); #endif case DIF_VAR_TID: #ifdef illumos /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (0); #endif return ((uint64_t)curthread->t_tid); case DIF_VAR_EXECARGS: { struct pargs *p_args = curthread->td_proc->p_args; if (p_args == NULL) return(0); return (dtrace_dif_varstrz( (uintptr_t) p_args->ar_args, p_args->ar_length, state, mstate)); } case DIF_VAR_EXECNAME: #ifdef illumos if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)(uintptr_t)p0.p_user.u_comm); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return (dtrace_dif_varstr( (uintptr_t)curthread->t_procp->p_user.u_comm, state, mstate)); #else return (dtrace_dif_varstr( (uintptr_t) curthread->td_proc->p_comm, state, mstate)); #endif case DIF_VAR_ZONENAME: #ifdef illumos if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)(uintptr_t)p0.p_zone->zone_name); /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) */ return (dtrace_dif_varstr( (uintptr_t)curthread->t_procp->p_zone->zone_name, state, mstate)); #else return (0); #endif case DIF_VAR_UID: if (!dtrace_priv_proc(state)) return (0); #ifdef illumos /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)p0.p_cred->cr_uid); #endif /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) * * Additionally, it is safe to dereference one's own process * credential, since this is never NULL after process birth. */ return ((uint64_t)curthread->t_procp->p_cred->cr_uid); case DIF_VAR_GID: if (!dtrace_priv_proc(state)) return (0); #ifdef illumos /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return ((uint64_t)p0.p_cred->cr_gid); #endif /* * It is always safe to dereference one's own t_procp pointer: * it always points to a valid, allocated proc structure. * (This is true because threads don't clean up their own * state -- they leave that task to whomever reaps them.) * * Additionally, it is safe to dereference one's own process * credential, since this is never NULL after process birth. */ return ((uint64_t)curthread->t_procp->p_cred->cr_gid); case DIF_VAR_ERRNO: { #ifdef illumos klwp_t *lwp; if (!dtrace_priv_proc(state)) return (0); /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU)) return (0); /* * It is always safe to dereference one's own t_lwp pointer in * the event that this pointer is non-NULL. (This is true * because threads and lwps don't clean up their own state -- * they leave that task to whomever reaps them.) */ if ((lwp = curthread->t_lwp) == NULL) return (0); return ((uint64_t)lwp->lwp_errno); #else return (curthread->td_errno); #endif } #ifndef illumos case DIF_VAR_CPU: { return curcpu; } #endif default: DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return (0); } } typedef enum dtrace_json_state { DTRACE_JSON_REST = 1, DTRACE_JSON_OBJECT, DTRACE_JSON_STRING, DTRACE_JSON_STRING_ESCAPE, DTRACE_JSON_STRING_ESCAPE_UNICODE, DTRACE_JSON_COLON, DTRACE_JSON_COMMA, DTRACE_JSON_VALUE, DTRACE_JSON_IDENTIFIER, DTRACE_JSON_NUMBER, DTRACE_JSON_NUMBER_FRAC, DTRACE_JSON_NUMBER_EXP, DTRACE_JSON_COLLECT_OBJECT } dtrace_json_state_t; /* * This function possesses just enough knowledge about JSON to extract a single * value from a JSON string and store it in the scratch buffer. It is able * to extract nested object values, and members of arrays by index. * * elemlist is a list of JSON keys, stored as packed NUL-terminated strings, to * be looked up as we descend into the object tree. e.g. * * foo[0].bar.baz[32] --> "foo" NUL "0" NUL "bar" NUL "baz" NUL "32" NUL * with nelems = 5. * * The run time of this function must be bounded above by strsize to limit the * amount of work done in probe context. As such, it is implemented as a * simple state machine, reading one character at a time using safe loads * until we find the requested element, hit a parsing error or run off the * end of the object or string. * * As there is no way for a subroutine to return an error without interrupting * clause execution, we simply return NULL in the event of a missing key or any * other error condition. Each NULL return in this function is commented with * the error condition it represents -- parsing or otherwise. * * The set of states for the state machine closely matches the JSON * specification (http://json.org/). Briefly: * * DTRACE_JSON_REST: * Skip whitespace until we find either a top-level Object, moving * to DTRACE_JSON_OBJECT; or an Array, moving to DTRACE_JSON_VALUE. * * DTRACE_JSON_OBJECT: * Locate the next key String in an Object. Sets a flag to denote * the next String as a key string and moves to DTRACE_JSON_STRING. * * DTRACE_JSON_COLON: * Skip whitespace until we find the colon that separates key Strings * from their values. Once found, move to DTRACE_JSON_VALUE. * * DTRACE_JSON_VALUE: * Detects the type of the next value (String, Number, Identifier, Object * or Array) and routes to the states that process that type. Here we also * deal with the element selector list if we are requested to traverse down * into the object tree. * * DTRACE_JSON_COMMA: * Skip whitespace until we find the comma that separates key-value pairs * in Objects (returning to DTRACE_JSON_OBJECT) or values in Arrays * (similarly DTRACE_JSON_VALUE). All following literal value processing * states return to this state at the end of their value, unless otherwise * noted. * * DTRACE_JSON_NUMBER, DTRACE_JSON_NUMBER_FRAC, DTRACE_JSON_NUMBER_EXP: * Processes a Number literal from the JSON, including any exponent * component that may be present. Numbers are returned as strings, which * may be passed to strtoll() if an integer is required. * * DTRACE_JSON_IDENTIFIER: * Processes a "true", "false" or "null" literal in the JSON. * * DTRACE_JSON_STRING, DTRACE_JSON_STRING_ESCAPE, * DTRACE_JSON_STRING_ESCAPE_UNICODE: * Processes a String literal from the JSON, whether the String denotes * a key, a value or part of a larger Object. Handles all escape sequences * present in the specification, including four-digit unicode characters, * but merely includes the escape sequence without converting it to the * actual escaped character. If the String is flagged as a key, we * move to DTRACE_JSON_COLON rather than DTRACE_JSON_COMMA. * * DTRACE_JSON_COLLECT_OBJECT: * This state collects an entire Object (or Array), correctly handling * embedded strings. If the full element selector list matches this nested * object, we return the Object in full as a string. If not, we use this * state to skip to the next value at this level and continue processing. * * NOTE: This function uses various macros from strtolctype.h to manipulate * digit values, etc -- these have all been checked to ensure they make * no additional function calls. */ static char * dtrace_json(uint64_t size, uintptr_t json, char *elemlist, int nelems, char *dest) { dtrace_json_state_t state = DTRACE_JSON_REST; int64_t array_elem = INT64_MIN; int64_t array_pos = 0; uint8_t escape_unicount = 0; boolean_t string_is_key = B_FALSE; boolean_t collect_object = B_FALSE; boolean_t found_key = B_FALSE; boolean_t in_array = B_FALSE; uint32_t braces = 0, brackets = 0; char *elem = elemlist; char *dd = dest; uintptr_t cur; for (cur = json; cur < json + size; cur++) { char cc = dtrace_load8(cur); if (cc == '\0') return (NULL); switch (state) { case DTRACE_JSON_REST: if (isspace(cc)) break; if (cc == '{') { state = DTRACE_JSON_OBJECT; break; } if (cc == '[') { in_array = B_TRUE; array_pos = 0; array_elem = dtrace_strtoll(elem, 10, size); found_key = array_elem == 0 ? B_TRUE : B_FALSE; state = DTRACE_JSON_VALUE; break; } /* * ERROR: expected to find a top-level object or array. */ return (NULL); case DTRACE_JSON_OBJECT: if (isspace(cc)) break; if (cc == '"') { state = DTRACE_JSON_STRING; string_is_key = B_TRUE; break; } /* * ERROR: either the object did not start with a key * string, or we've run off the end of the object * without finding the requested key. */ return (NULL); case DTRACE_JSON_STRING: if (cc == '\\') { *dd++ = '\\'; state = DTRACE_JSON_STRING_ESCAPE; break; } if (cc == '"') { if (collect_object) { /* * We don't reset the dest here, as * the string is part of a larger * object being collected. */ *dd++ = cc; collect_object = B_FALSE; state = DTRACE_JSON_COLLECT_OBJECT; break; } *dd = '\0'; dd = dest; /* reset string buffer */ if (string_is_key) { if (dtrace_strncmp(dest, elem, size) == 0) found_key = B_TRUE; } else if (found_key) { if (nelems > 1) { /* * We expected an object, not * this string. */ return (NULL); } return (dest); } state = string_is_key ? DTRACE_JSON_COLON : DTRACE_JSON_COMMA; string_is_key = B_FALSE; break; } *dd++ = cc; break; case DTRACE_JSON_STRING_ESCAPE: *dd++ = cc; if (cc == 'u') { escape_unicount = 0; state = DTRACE_JSON_STRING_ESCAPE_UNICODE; } else { state = DTRACE_JSON_STRING; } break; case DTRACE_JSON_STRING_ESCAPE_UNICODE: if (!isxdigit(cc)) { /* * ERROR: invalid unicode escape, expected * four valid hexidecimal digits. */ return (NULL); } *dd++ = cc; if (++escape_unicount == 4) state = DTRACE_JSON_STRING; break; case DTRACE_JSON_COLON: if (isspace(cc)) break; if (cc == ':') { state = DTRACE_JSON_VALUE; break; } /* * ERROR: expected a colon. */ return (NULL); case DTRACE_JSON_COMMA: if (isspace(cc)) break; if (cc == ',') { if (in_array) { state = DTRACE_JSON_VALUE; if (++array_pos == array_elem) found_key = B_TRUE; } else { state = DTRACE_JSON_OBJECT; } break; } /* * ERROR: either we hit an unexpected character, or * we reached the end of the object or array without * finding the requested key. */ return (NULL); case DTRACE_JSON_IDENTIFIER: if (islower(cc)) { *dd++ = cc; break; } *dd = '\0'; dd = dest; /* reset string buffer */ if (dtrace_strncmp(dest, "true", 5) == 0 || dtrace_strncmp(dest, "false", 6) == 0 || dtrace_strncmp(dest, "null", 5) == 0) { if (found_key) { if (nelems > 1) { /* * ERROR: We expected an object, * not this identifier. */ return (NULL); } return (dest); } else { cur--; state = DTRACE_JSON_COMMA; break; } } /* * ERROR: we did not recognise the identifier as one * of those in the JSON specification. */ return (NULL); case DTRACE_JSON_NUMBER: if (cc == '.') { *dd++ = cc; state = DTRACE_JSON_NUMBER_FRAC; break; } if (cc == 'x' || cc == 'X') { /* * ERROR: specification explicitly excludes * hexidecimal or octal numbers. */ return (NULL); } /* FALLTHRU */ case DTRACE_JSON_NUMBER_FRAC: if (cc == 'e' || cc == 'E') { *dd++ = cc; state = DTRACE_JSON_NUMBER_EXP; break; } if (cc == '+' || cc == '-') { /* * ERROR: expect sign as part of exponent only. */ return (NULL); } /* FALLTHRU */ case DTRACE_JSON_NUMBER_EXP: if (isdigit(cc) || cc == '+' || cc == '-') { *dd++ = cc; break; } *dd = '\0'; dd = dest; /* reset string buffer */ if (found_key) { if (nelems > 1) { /* * ERROR: We expected an object, not * this number. */ return (NULL); } return (dest); } cur--; state = DTRACE_JSON_COMMA; break; case DTRACE_JSON_VALUE: if (isspace(cc)) break; if (cc == '{' || cc == '[') { if (nelems > 1 && found_key) { in_array = cc == '[' ? B_TRUE : B_FALSE; /* * If our element selector directs us * to descend into this nested object, * then move to the next selector * element in the list and restart the * state machine. */ while (*elem != '\0') elem++; elem++; /* skip the inter-element NUL */ nelems--; dd = dest; if (in_array) { state = DTRACE_JSON_VALUE; array_pos = 0; array_elem = dtrace_strtoll( elem, 10, size); found_key = array_elem == 0 ? B_TRUE : B_FALSE; } else { found_key = B_FALSE; state = DTRACE_JSON_OBJECT; } break; } /* * Otherwise, we wish to either skip this * nested object or return it in full. */ if (cc == '[') brackets = 1; else braces = 1; *dd++ = cc; state = DTRACE_JSON_COLLECT_OBJECT; break; } if (cc == '"') { state = DTRACE_JSON_STRING; break; } if (islower(cc)) { /* * Here we deal with true, false and null. */ *dd++ = cc; state = DTRACE_JSON_IDENTIFIER; break; } if (cc == '-' || isdigit(cc)) { *dd++ = cc; state = DTRACE_JSON_NUMBER; break; } /* * ERROR: unexpected character at start of value. */ return (NULL); case DTRACE_JSON_COLLECT_OBJECT: if (cc == '\0') /* * ERROR: unexpected end of input. */ return (NULL); *dd++ = cc; if (cc == '"') { collect_object = B_TRUE; state = DTRACE_JSON_STRING; break; } if (cc == ']') { if (brackets-- == 0) { /* * ERROR: unbalanced brackets. */ return (NULL); } } else if (cc == '}') { if (braces-- == 0) { /* * ERROR: unbalanced braces. */ return (NULL); } } else if (cc == '{') { braces++; } else if (cc == '[') { brackets++; } if (brackets == 0 && braces == 0) { if (found_key) { *dd = '\0'; return (dest); } dd = dest; /* reset string buffer */ state = DTRACE_JSON_COMMA; } break; } } return (NULL); } /* * Emulate the execution of DTrace ID subroutines invoked by the call opcode. * Notice that we don't bother validating the proper number of arguments or * their types in the tuple stack. This isn't needed because all argument * interpretation is safe because of our load safety -- the worst that can * happen is that a bogus program can obtain bogus results. */ static void dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs, dtrace_key_t *tupregs, int nargs, dtrace_mstate_t *mstate, dtrace_state_t *state) { volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; dtrace_vstate_t *vstate = &state->dts_vstate; #ifdef illumos union { mutex_impl_t mi; uint64_t mx; } m; union { krwlock_t ri; uintptr_t rw; } r; #else struct thread *lowner; union { struct lock_object *li; uintptr_t lx; } l; #endif switch (subr) { case DIF_SUBR_RAND: regs[rd] = (dtrace_gethrtime() * 2416 + 374441) % 1771875; break; #ifdef illumos case DIF_SUBR_MUTEX_OWNED: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi)) regs[rd] = MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER; else regs[rd] = LOCK_HELD(&m.mi.m_spin.m_spinlock); break; case DIF_SUBR_MUTEX_OWNER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); if (MUTEX_TYPE_ADAPTIVE(&m.mi) && MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER) regs[rd] = (uintptr_t)MUTEX_OWNER(&m.mi); else regs[rd] = 0; break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_ADAPTIVE(&m.mi); break; case DIF_SUBR_MUTEX_TYPE_SPIN: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t), mstate, vstate)) { regs[rd] = 0; break; } m.mx = dtrace_load64(tupregs[0].dttk_value); regs[rd] = MUTEX_TYPE_SPIN(&m.mi); break; case DIF_SUBR_RW_READ_HELD: { uintptr_t tmp; if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_READ_HELD(&r.ri, tmp); break; } case DIF_SUBR_RW_WRITE_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_WRITE_HELD(&r.ri); break; case DIF_SUBR_RW_ISWRITER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t), mstate, vstate)) { regs[rd] = 0; break; } r.rw = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = _RW_ISWRITER(&r.ri); break; #else /* !illumos */ case DIF_SUBR_MUTEX_OWNED: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct lock_object), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); break; case DIF_SUBR_MUTEX_OWNER: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct lock_object), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); regs[rd] = (uintptr_t)lowner; break; case DIF_SUBR_MUTEX_TYPE_ADAPTIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); /* XXX - should be only LC_SLEEPABLE? */ regs[rd] = (LOCK_CLASS(l.li)->lc_flags & (LC_SLEEPLOCK | LC_SLEEPABLE)) != 0; break; case DIF_SUBR_MUTEX_TYPE_SPIN: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (struct mtx), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = (LOCK_CLASS(l.li)->lc_flags & LC_SPINLOCK) != 0; break; case DIF_SUBR_RW_READ_HELD: case DIF_SUBR_SX_SHARED_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr((uintptr_t)&tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && lowner == NULL; break; case DIF_SUBR_RW_WRITE_HELD: case DIF_SUBR_SX_EXCLUSIVE_HELD: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr(tupregs[0].dttk_value); LOCK_CLASS(l.li)->lc_owner(l.li, &lowner); regs[rd] = (lowner == curthread); break; case DIF_SUBR_RW_ISWRITER: case DIF_SUBR_SX_ISEXCLUSIVE: if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t), mstate, vstate)) { regs[rd] = 0; break; } l.lx = dtrace_loadptr(tupregs[0].dttk_value); regs[rd] = LOCK_CLASS(l.li)->lc_owner(l.li, &lowner) && lowner != NULL; break; #endif /* illumos */ case DIF_SUBR_BCOPY: { /* * We need to be sure that the destination is in the scratch * region -- no other region is allowed. */ uintptr_t src = tupregs[0].dttk_value; uintptr_t dest = tupregs[1].dttk_value; size_t size = tupregs[2].dttk_value; if (!dtrace_inscratch(dest, size, mstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (!dtrace_canload(src, size, mstate, vstate)) { regs[rd] = 0; break; } dtrace_bcopy((void *)src, (void *)dest, size); break; } case DIF_SUBR_ALLOCA: case DIF_SUBR_COPYIN: { uintptr_t dest = P2ROUNDUP(mstate->dtms_scratch_ptr, 8); uint64_t size = tupregs[subr == DIF_SUBR_ALLOCA ? 0 : 1].dttk_value; size_t scratch_size = (dest - mstate->dtms_scratch_ptr) + size; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ /* * Rounding up the user allocation size could have overflowed * a large, bogus allocation (like -1ULL) to 0. */ if (scratch_size < size || !DTRACE_INSCRATCH(mstate, scratch_size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (subr == DIF_SUBR_COPYIN) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyin(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } mstate->dtms_scratch_ptr += scratch_size; regs[rd] = dest; break; } case DIF_SUBR_COPYINTO: { uint64_t size = tupregs[1].dttk_value; uintptr_t dest = tupregs[2].dttk_value; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ if (!dtrace_inscratch(dest, size, mstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyin(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; } case DIF_SUBR_COPYINSTR: { uintptr_t dest = mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; if (nargs > 1 && tupregs[1].dttk_value < size) size = tupregs[1].dttk_value + 1; /* * This action doesn't require any credential checks since * probes will not activate in user contexts to which the * enabling user does not have permissions. */ if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyinstr(tupregs[0].dttk_value, dest, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); ((char *)dest)[size - 1] = '\0'; mstate->dtms_scratch_ptr += size; regs[rd] = dest; break; } #ifdef illumos case DIF_SUBR_MSGSIZE: case DIF_SUBR_MSGDSIZE: { uintptr_t baddr = tupregs[0].dttk_value, daddr; uintptr_t wptr, rptr; size_t count = 0; int cont = 0; while (baddr != 0 && !(*flags & CPU_DTRACE_FAULT)) { if (!dtrace_canload(baddr, sizeof (mblk_t), mstate, vstate)) { regs[rd] = 0; break; } wptr = dtrace_loadptr(baddr + offsetof(mblk_t, b_wptr)); rptr = dtrace_loadptr(baddr + offsetof(mblk_t, b_rptr)); if (wptr < rptr) { *flags |= CPU_DTRACE_BADADDR; *illval = tupregs[0].dttk_value; break; } daddr = dtrace_loadptr(baddr + offsetof(mblk_t, b_datap)); baddr = dtrace_loadptr(baddr + offsetof(mblk_t, b_cont)); /* * We want to prevent against denial-of-service here, * so we're only going to search the list for * dtrace_msgdsize_max mblks. */ if (cont++ > dtrace_msgdsize_max) { *flags |= CPU_DTRACE_ILLOP; break; } if (subr == DIF_SUBR_MSGDSIZE) { if (dtrace_load8(daddr + offsetof(dblk_t, db_type)) != M_DATA) continue; } count += wptr - rptr; } if (!(*flags & CPU_DTRACE_FAULT)) regs[rd] = count; break; } #endif case DIF_SUBR_PROGENYOF: { pid_t pid = tupregs[0].dttk_value; proc_t *p; int rval = 0; DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); for (p = curthread->t_procp; p != NULL; p = p->p_parent) { #ifdef illumos if (p->p_pidp->pid_id == pid) { #else if (p->p_pid == pid) { #endif rval = 1; break; } } DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); regs[rd] = rval; break; } case DIF_SUBR_SPECULATION: regs[rd] = dtrace_speculation(state); break; case DIF_SUBR_COPYOUT: { uintptr_t kaddr = tupregs[0].dttk_value; uintptr_t uaddr = tupregs[1].dttk_value; uint64_t size = tupregs[2].dttk_value; if (!dtrace_destructive_disallow && dtrace_priv_proc_control(state) && !dtrace_istoxic(kaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyout(kaddr, uaddr, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } break; } case DIF_SUBR_COPYOUTSTR: { uintptr_t kaddr = tupregs[0].dttk_value; uintptr_t uaddr = tupregs[1].dttk_value; uint64_t size = tupregs[2].dttk_value; if (!dtrace_destructive_disallow && dtrace_priv_proc_control(state) && !dtrace_istoxic(kaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_copyoutstr(kaddr, uaddr, size, flags); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); } break; } case DIF_SUBR_STRLEN: { size_t sz; uintptr_t addr = (uintptr_t)tupregs[0].dttk_value; sz = dtrace_strlen((char *)addr, state->dts_options[DTRACEOPT_STRSIZE]); if (!dtrace_canload(addr, sz + 1, mstate, vstate)) { regs[rd] = 0; break; } regs[rd] = sz; break; } case DIF_SUBR_STRCHR: case DIF_SUBR_STRRCHR: { /* * We're going to iterate over the string looking for the * specified character. We will iterate until we have reached * the string length or we have found the character. If this * is DIF_SUBR_STRRCHR, we will look for the last occurrence * of the specified character instead of the first. */ uintptr_t saddr = tupregs[0].dttk_value; uintptr_t addr = tupregs[0].dttk_value; uintptr_t limit = addr + state->dts_options[DTRACEOPT_STRSIZE]; char c, target = (char)tupregs[1].dttk_value; for (regs[rd] = 0; addr < limit; addr++) { if ((c = dtrace_load8(addr)) == target) { regs[rd] = addr; if (subr == DIF_SUBR_STRCHR) break; } if (c == '\0') break; } if (!dtrace_canload(saddr, addr - saddr, mstate, vstate)) { regs[rd] = 0; break; } break; } case DIF_SUBR_STRSTR: case DIF_SUBR_INDEX: case DIF_SUBR_RINDEX: { /* * We're going to iterate over the string looking for the * specified string. We will iterate until we have reached * the string length or we have found the string. (Yes, this * is done in the most naive way possible -- but considering * that the string we're searching for is likely to be * relatively short, the complexity of Rabin-Karp or similar * hardly seems merited.) */ char *addr = (char *)(uintptr_t)tupregs[0].dttk_value; char *substr = (char *)(uintptr_t)tupregs[1].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; size_t len = dtrace_strlen(addr, size); size_t sublen = dtrace_strlen(substr, size); char *limit = addr + len, *orig = addr; int notfound = subr == DIF_SUBR_STRSTR ? 0 : -1; int inc = 1; regs[rd] = notfound; if (!dtrace_canload((uintptr_t)addr, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!dtrace_canload((uintptr_t)substr, sublen + 1, mstate, vstate)) { regs[rd] = 0; break; } /* * strstr() and index()/rindex() have similar semantics if * both strings are the empty string: strstr() returns a * pointer to the (empty) string, and index() and rindex() * both return index 0 (regardless of any position argument). */ if (sublen == 0 && len == 0) { if (subr == DIF_SUBR_STRSTR) regs[rd] = (uintptr_t)addr; else regs[rd] = 0; break; } if (subr != DIF_SUBR_STRSTR) { if (subr == DIF_SUBR_RINDEX) { limit = orig - 1; addr += len; inc = -1; } /* * Both index() and rindex() take an optional position * argument that denotes the starting position. */ if (nargs == 3) { int64_t pos = (int64_t)tupregs[2].dttk_value; /* * If the position argument to index() is * negative, Perl implicitly clamps it at * zero. This semantic is a little surprising * given the special meaning of negative * positions to similar Perl functions like * substr(), but it appears to reflect a * notion that index() can start from a * negative index and increment its way up to * the string. Given this notion, Perl's * rindex() is at least self-consistent in * that it implicitly clamps positions greater * than the string length to be the string * length. Where Perl completely loses * coherence, however, is when the specified * substring is the empty string (""). In * this case, even if the position is * negative, rindex() returns 0 -- and even if * the position is greater than the length, * index() returns the string length. These * semantics violate the notion that index() * should never return a value less than the * specified position and that rindex() should * never return a value greater than the * specified position. (One assumes that * these semantics are artifacts of Perl's * implementation and not the results of * deliberate design -- it beggars belief that * even Larry Wall could desire such oddness.) * While in the abstract one would wish for * consistent position semantics across * substr(), index() and rindex() -- or at the * very least self-consistent position * semantics for index() and rindex() -- we * instead opt to keep with the extant Perl * semantics, in all their broken glory. (Do * we have more desire to maintain Perl's * semantics than Perl does? Probably.) */ if (subr == DIF_SUBR_RINDEX) { if (pos < 0) { if (sublen == 0) regs[rd] = 0; break; } if (pos > len) pos = len; } else { if (pos < 0) pos = 0; if (pos >= len) { if (sublen == 0) regs[rd] = len; break; } } addr = orig + pos; } } for (regs[rd] = notfound; addr != limit; addr += inc) { if (dtrace_strncmp(addr, substr, sublen) == 0) { if (subr != DIF_SUBR_STRSTR) { /* * As D index() and rindex() are * modeled on Perl (and not on awk), * we return a zero-based (and not a * one-based) index. (For you Perl * weenies: no, we're not going to add * $[ -- and shouldn't you be at a con * or something?) */ regs[rd] = (uintptr_t)(addr - orig); break; } ASSERT(subr == DIF_SUBR_STRSTR); regs[rd] = (uintptr_t)addr; break; } } break; } case DIF_SUBR_STRTOK: { uintptr_t addr = tupregs[0].dttk_value; uintptr_t tokaddr = tupregs[1].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t limit, toklimit = tokaddr + size; uint8_t c = 0, tokmap[32]; /* 256 / 8 */ char *dest = (char *)mstate->dtms_scratch_ptr; int i; /* * Check both the token buffer and (later) the input buffer, * since both could be non-scratch addresses. */ if (!dtrace_strcanload(tokaddr, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (addr == 0) { /* * If the address specified is NULL, we use our saved * strtok pointer from the mstate. Note that this * means that the saved strtok pointer is _only_ * valid within multiple enablings of the same probe -- * it behaves like an implicit clause-local variable. */ addr = mstate->dtms_strtok; } else { /* * If the user-specified address is non-NULL we must * access check it. This is the only time we have * a chance to do so, since this address may reside * in the string table of this clause-- future calls * (when we fetch addr from mstate->dtms_strtok) * would fail this access check. */ if (!dtrace_strcanload(addr, size, mstate, vstate)) { regs[rd] = 0; break; } } /* * First, zero the token map, and then process the token * string -- setting a bit in the map for every character * found in the token string. */ for (i = 0; i < sizeof (tokmap); i++) tokmap[i] = 0; for (; tokaddr < toklimit; tokaddr++) { if ((c = dtrace_load8(tokaddr)) == '\0') break; ASSERT((c >> 3) < sizeof (tokmap)); tokmap[c >> 3] |= (1 << (c & 0x7)); } for (limit = addr + size; addr < limit; addr++) { /* * We're looking for a character that is _not_ contained * in the token string. */ if ((c = dtrace_load8(addr)) == '\0') break; if (!(tokmap[c >> 3] & (1 << (c & 0x7)))) break; } if (c == '\0') { /* * We reached the end of the string without finding * any character that was not in the token string. * We return NULL in this case, and we set the saved * address to NULL as well. */ regs[rd] = 0; mstate->dtms_strtok = 0; break; } /* * From here on, we're copying into the destination string. */ for (i = 0; addr < limit && i < size - 1; addr++) { if ((c = dtrace_load8(addr)) == '\0') break; if (tokmap[c >> 3] & (1 << (c & 0x7))) break; ASSERT(i < size); dest[i++] = c; } ASSERT(i < size); dest[i] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; mstate->dtms_strtok = addr; break; } case DIF_SUBR_SUBSTR: { uintptr_t s = tupregs[0].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *d = (char *)mstate->dtms_scratch_ptr; int64_t index = (int64_t)tupregs[1].dttk_value; int64_t remaining = (int64_t)tupregs[2].dttk_value; size_t len = dtrace_strlen((char *)s, size); int64_t i; if (!dtrace_canload(s, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if (nargs <= 2) remaining = (int64_t)size; if (index < 0) { index += len; if (index < 0 && index + remaining > 0) { remaining += index; index = 0; } } if (index >= len || index < 0) { remaining = 0; } else if (remaining < 0) { remaining += len - index; } else if (index + remaining > size) { remaining = size - index; } for (i = 0; i < remaining; i++) { if ((d[i] = dtrace_load8(s + index + i)) == '\0') break; } d[i] = '\0'; mstate->dtms_scratch_ptr += size; regs[rd] = (uintptr_t)d; break; } case DIF_SUBR_JSON: { uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t json = tupregs[0].dttk_value; size_t jsonlen = dtrace_strlen((char *)json, size); uintptr_t elem = tupregs[1].dttk_value; size_t elemlen = dtrace_strlen((char *)elem, size); char *dest = (char *)mstate->dtms_scratch_ptr; char *elemlist = (char *)mstate->dtms_scratch_ptr + jsonlen + 1; char *ee = elemlist; int nelems = 1; uintptr_t cur; if (!dtrace_canload(json, jsonlen + 1, mstate, vstate) || !dtrace_canload(elem, elemlen + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } /* * Read the element selector and split it up into a packed list * of strings. */ for (cur = elem; cur < elem + elemlen; cur++) { char cc = dtrace_load8(cur); if (cur == elem && cc == '[') { /* * If the first element selector key is * actually an array index then ignore the * bracket. */ continue; } if (cc == ']') continue; if (cc == '.' || cc == '[') { nelems++; cc = '\0'; } *ee++ = cc; } *ee++ = '\0'; if ((regs[rd] = (uintptr_t)dtrace_json(size, json, elemlist, nelems, dest)) != 0) mstate->dtms_scratch_ptr += jsonlen + 1; break; } case DIF_SUBR_TOUPPER: case DIF_SUBR_TOLOWER: { uintptr_t s = tupregs[0].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *dest = (char *)mstate->dtms_scratch_ptr, c; size_t len = dtrace_strlen((char *)s, size); char lower, upper, convert; int64_t i; if (subr == DIF_SUBR_TOUPPER) { lower = 'a'; upper = 'z'; convert = 'A'; } else { lower = 'A'; upper = 'Z'; convert = 'a'; } if (!dtrace_canload(s, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (i = 0; i < size - 1; i++) { if ((c = dtrace_load8(s + i)) == '\0') break; if (c >= lower && c <= upper) c = convert + (c - lower); dest[i] = c; } ASSERT(i < size); dest[i] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } #ifdef illumos case DIF_SUBR_GETMAJOR: #ifdef _LP64 regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR64) & MAXMAJ64; #else regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR) & MAXMAJ; #endif break; case DIF_SUBR_GETMINOR: #ifdef _LP64 regs[rd] = tupregs[0].dttk_value & MAXMIN64; #else regs[rd] = tupregs[0].dttk_value & MAXMIN; #endif break; case DIF_SUBR_DDI_PATHNAME: { /* * This one is a galactic mess. We are going to roughly * emulate ddi_pathname(), but it's made more complicated * by the fact that we (a) want to include the minor name and * (b) must proceed iteratively instead of recursively. */ uintptr_t dest = mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; char *start = (char *)dest, *end = start + size - 1; uintptr_t daddr = tupregs[0].dttk_value; int64_t minor = (int64_t)tupregs[1].dttk_value; char *s; int i, len, depth = 0; /* * Due to all the pointer jumping we do and context we must * rely upon, we just mandate that the user must have kernel * read privileges to use this routine. */ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) == 0) { *flags |= CPU_DTRACE_KPRIV; *illval = daddr; regs[rd] = 0; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } *end = '\0'; /* * We want to have a name for the minor. In order to do this, * we need to walk the minor list from the devinfo. We want * to be sure that we don't infinitely walk a circular list, * so we check for circularity by sending a scout pointer * ahead two elements for every element that we iterate over; * if the list is circular, these will ultimately point to the * same element. You may recognize this little trick as the * answer to a stupid interview question -- one that always * seems to be asked by those who had to have it laboriously * explained to them, and who can't even concisely describe * the conditions under which one would be forced to resort to * this technique. Needless to say, those conditions are * found here -- and probably only here. Is this the only use * of this infamous trick in shipping, production code? If it * isn't, it probably should be... */ if (minor != -1) { uintptr_t maddr = dtrace_loadptr(daddr + offsetof(struct dev_info, devi_minor)); uintptr_t next = offsetof(struct ddi_minor_data, next); uintptr_t name = offsetof(struct ddi_minor_data, d_minor) + offsetof(struct ddi_minor, name); uintptr_t dev = offsetof(struct ddi_minor_data, d_minor) + offsetof(struct ddi_minor, dev); uintptr_t scout; if (maddr != NULL) scout = dtrace_loadptr(maddr + next); while (maddr != NULL && !(*flags & CPU_DTRACE_FAULT)) { uint64_t m; #ifdef _LP64 m = dtrace_load64(maddr + dev) & MAXMIN64; #else m = dtrace_load32(maddr + dev) & MAXMIN; #endif if (m != minor) { maddr = dtrace_loadptr(maddr + next); if (scout == NULL) continue; scout = dtrace_loadptr(scout + next); if (scout == NULL) continue; scout = dtrace_loadptr(scout + next); if (scout == NULL) continue; if (scout == maddr) { *flags |= CPU_DTRACE_ILLOP; break; } continue; } /* * We have the minor data. Now we need to * copy the minor's name into the end of the * pathname. */ s = (char *)dtrace_loadptr(maddr + name); len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if (len != 0) { if ((end -= (len + 1)) < start) break; *end = ':'; } for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); break; } } while (daddr != NULL && !(*flags & CPU_DTRACE_FAULT)) { ddi_node_state_t devi_state; devi_state = dtrace_load32(daddr + offsetof(struct dev_info, devi_node_state)); if (*flags & CPU_DTRACE_FAULT) break; if (devi_state >= DS_INITIALIZED) { s = (char *)dtrace_loadptr(daddr + offsetof(struct dev_info, devi_addr)); len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if (len != 0) { if ((end -= (len + 1)) < start) break; *end = '@'; } for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); } /* * Now for the node name... */ s = (char *)dtrace_loadptr(daddr + offsetof(struct dev_info, devi_node_name)); daddr = dtrace_loadptr(daddr + offsetof(struct dev_info, devi_parent)); /* * If our parent is NULL (that is, if we're the root * node), we're going to use the special path * "devices". */ if (daddr == 0) s = "devices"; len = dtrace_strlen(s, size); if (*flags & CPU_DTRACE_FAULT) break; if ((end -= (len + 1)) < start) break; for (i = 1; i <= len; i++) end[i] = dtrace_load8((uintptr_t)s++); *end = '/'; if (depth++ > dtrace_devdepth_max) { *flags |= CPU_DTRACE_ILLOP; break; } } if (end < start) DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); if (daddr == 0) { regs[rd] = (uintptr_t)end; mstate->dtms_scratch_ptr += size; } break; } #endif case DIF_SUBR_STRJOIN: { char *d = (char *)mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t s1 = tupregs[0].dttk_value; uintptr_t s2 = tupregs[1].dttk_value; int i = 0; if (!dtrace_strcanload(s1, size, mstate, vstate) || !dtrace_strcanload(s2, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (;;) { if (i >= size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if ((d[i++] = dtrace_load8(s1++)) == '\0') { i--; break; } } for (;;) { if (i >= size) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } if ((d[i++] = dtrace_load8(s2++)) == '\0') break; } if (i < size) { mstate->dtms_scratch_ptr += i; regs[rd] = (uintptr_t)d; } break; } case DIF_SUBR_STRTOLL: { uintptr_t s = tupregs[0].dttk_value; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; int base = 10; if (nargs > 1) { if ((base = tupregs[1].dttk_value) <= 1 || base > ('z' - 'a' + 1) + ('9' - '0' + 1)) { *flags |= CPU_DTRACE_ILLOP; break; } } if (!dtrace_strcanload(s, size, mstate, vstate)) { regs[rd] = INT64_MIN; break; } regs[rd] = dtrace_strtoll((char *)s, base, size); break; } case DIF_SUBR_LLTOSTR: { int64_t i = (int64_t)tupregs[0].dttk_value; uint64_t val, digit; uint64_t size = 65; /* enough room for 2^64 in binary */ char *end = (char *)mstate->dtms_scratch_ptr + size - 1; int base = 10; if (nargs > 1) { if ((base = tupregs[1].dttk_value) <= 1 || base > ('z' - 'a' + 1) + ('9' - '0' + 1)) { *flags |= CPU_DTRACE_ILLOP; break; } } val = (base == 10 && i < 0) ? i * -1 : i; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } for (*end-- = '\0'; val; val /= base) { if ((digit = val % base) <= '9' - '0') { *end-- = '0' + digit; } else { *end-- = 'a' + (digit - ('9' - '0') - 1); } } if (i == 0 && base == 16) *end-- = '0'; if (base == 16) *end-- = 'x'; if (i == 0 || base == 8 || base == 16) *end-- = '0'; if (i < 0 && base == 10) *end-- = '-'; regs[rd] = (uintptr_t)end + 1; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_HTONS: case DIF_SUBR_NTOHS: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint16_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_16((uint16_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_HTONL: case DIF_SUBR_NTOHL: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint32_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_32((uint32_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_HTONLL: case DIF_SUBR_NTOHLL: #if BYTE_ORDER == BIG_ENDIAN regs[rd] = (uint64_t)tupregs[0].dttk_value; #else regs[rd] = DT_BSWAP_64((uint64_t)tupregs[0].dttk_value); #endif break; case DIF_SUBR_DIRNAME: case DIF_SUBR_BASENAME: { char *dest = (char *)mstate->dtms_scratch_ptr; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t src = tupregs[0].dttk_value; int i, j, len = dtrace_strlen((char *)src, size); int lastbase = -1, firstbase = -1, lastdir = -1; int start, end; if (!dtrace_canload(src, len + 1, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } /* * The basename and dirname for a zero-length string is * defined to be "." */ if (len == 0) { len = 1; src = (uintptr_t)"."; } /* * Start from the back of the string, moving back toward the * front until we see a character that isn't a slash. That * character is the last character in the basename. */ for (i = len - 1; i >= 0; i--) { if (dtrace_load8(src + i) != '/') break; } if (i >= 0) lastbase = i; /* * Starting from the last character in the basename, move * towards the front until we find a slash. The character * that we processed immediately before that is the first * character in the basename. */ for (; i >= 0; i--) { if (dtrace_load8(src + i) == '/') break; } if (i >= 0) firstbase = i + 1; /* * Now keep going until we find a non-slash character. That * character is the last character in the dirname. */ for (; i >= 0; i--) { if (dtrace_load8(src + i) != '/') break; } if (i >= 0) lastdir = i; ASSERT(!(lastbase == -1 && firstbase != -1)); ASSERT(!(firstbase == -1 && lastdir != -1)); if (lastbase == -1) { /* * We didn't find a non-slash character. We know that * the length is non-zero, so the whole string must be * slashes. In either the dirname or the basename * case, we return '/'. */ ASSERT(firstbase == -1); firstbase = lastbase = lastdir = 0; } if (firstbase == -1) { /* * The entire string consists only of a basename * component. If we're looking for dirname, we need * to change our string to be just "."; if we're * looking for a basename, we'll just set the first * character of the basename to be 0. */ if (subr == DIF_SUBR_DIRNAME) { ASSERT(lastdir == -1); src = (uintptr_t)"."; lastdir = 0; } else { firstbase = 0; } } if (subr == DIF_SUBR_DIRNAME) { if (lastdir == -1) { /* * We know that we have a slash in the name -- * or lastdir would be set to 0, above. And * because lastdir is -1, we know that this * slash must be the first character. (That * is, the full string must be of the form * "/basename".) In this case, the last * character of the directory name is 0. */ lastdir = 0; } start = 0; end = lastdir; } else { ASSERT(subr == DIF_SUBR_BASENAME); ASSERT(firstbase != -1 && lastbase != -1); start = firstbase; end = lastbase; } for (i = start, j = 0; i <= end && j < size - 1; i++, j++) dest[j] = dtrace_load8(src + i); dest[j] = '\0'; regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_GETF: { uintptr_t fd = tupregs[0].dttk_value; struct filedesc *fdp; file_t *fp; if (!dtrace_priv_proc(state)) { regs[rd] = 0; break; } fdp = curproc->p_fd; FILEDESC_SLOCK(fdp); fp = fget_locked(fdp, fd); mstate->dtms_getf = fp; regs[rd] = (uintptr_t)fp; FILEDESC_SUNLOCK(fdp); break; } case DIF_SUBR_CLEANPATH: { char *dest = (char *)mstate->dtms_scratch_ptr, c; uint64_t size = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t src = tupregs[0].dttk_value; int i = 0, j = 0; #ifdef illumos zone_t *z; #endif if (!dtrace_strcanload(src, size, mstate, vstate)) { regs[rd] = 0; break; } if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } /* * Move forward, loading each character. */ do { c = dtrace_load8(src + i++); next: if (j + 5 >= size) /* 5 = strlen("/..c\0") */ break; if (c != '/') { dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c == '/') { /* * We have two slashes -- we can just advance * to the next character. */ goto next; } if (c != '.') { /* * This is not "." and it's not ".." -- we can * just store the "/" and this character and * drive on. */ dest[j++] = '/'; dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c == '/') { /* * This is a "/./" component. We're not going * to store anything in the destination buffer; * we're just going to go to the next component. */ goto next; } if (c != '.') { /* * This is not ".." -- we can just store the * "/." and this character and continue * processing. */ dest[j++] = '/'; dest[j++] = '.'; dest[j++] = c; continue; } c = dtrace_load8(src + i++); if (c != '/' && c != '\0') { /* * This is not ".." -- it's "..[mumble]". * We'll store the "/.." and this character * and continue processing. */ dest[j++] = '/'; dest[j++] = '.'; dest[j++] = '.'; dest[j++] = c; continue; } /* * This is "/../" or "/..\0". We need to back up * our destination pointer until we find a "/". */ i--; while (j != 0 && dest[--j] != '/') continue; if (c == '\0') dest[++j] = '/'; } while (c != '\0'); dest[j] = '\0'; #ifdef illumos if (mstate->dtms_getf != NULL && !(mstate->dtms_access & DTRACE_ACCESS_KERNEL) && (z = state->dts_cred.dcr_cred->cr_zone) != kcred->cr_zone) { /* * If we've done a getf() as a part of this ECB and we * don't have kernel access (and we're not in the global * zone), check if the path we cleaned up begins with * the zone's root path, and trim it off if so. Note * that this is an output cleanliness issue, not a * security issue: knowing one's zone root path does * not enable privilege escalation. */ if (strstr(dest, z->zone_rootpath) == dest) dest += strlen(z->zone_rootpath) - 1; } #endif regs[rd] = (uintptr_t)dest; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_INET_NTOA: case DIF_SUBR_INET_NTOA6: case DIF_SUBR_INET_NTOP: { size_t size; int af, argi, i; char *base, *end; if (subr == DIF_SUBR_INET_NTOP) { af = (int)tupregs[0].dttk_value; argi = 1; } else { af = subr == DIF_SUBR_INET_NTOA ? AF_INET: AF_INET6; argi = 0; } if (af == AF_INET) { ipaddr_t ip4; uint8_t *ptr8, val; /* * Safely load the IPv4 address. */ ip4 = dtrace_load32(tupregs[argi].dttk_value); /* * Check an IPv4 string will fit in scratch. */ size = INET_ADDRSTRLEN; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } base = (char *)mstate->dtms_scratch_ptr; end = (char *)mstate->dtms_scratch_ptr + size - 1; /* * Stringify as a dotted decimal quad. */ *end-- = '\0'; ptr8 = (uint8_t *)&ip4; for (i = 3; i >= 0; i--) { val = ptr8[i]; if (val == 0) { *end-- = '0'; } else { for (; val; val /= 10) { *end-- = '0' + (val % 10); } } if (i > 0) *end-- = '.'; } ASSERT(end + 1 >= base); } else if (af == AF_INET6) { struct in6_addr ip6; int firstzero, tryzero, numzero, v6end; uint16_t val; const char digits[] = "0123456789abcdef"; /* * Stringify using RFC 1884 convention 2 - 16 bit * hexadecimal values with a zero-run compression. * Lower case hexadecimal digits are used. * eg, fe80::214:4fff:fe0b:76c8. * The IPv4 embedded form is returned for inet_ntop, * just the IPv4 string is returned for inet_ntoa6. */ /* * Safely load the IPv6 address. */ dtrace_bcopy( (void *)(uintptr_t)tupregs[argi].dttk_value, (void *)(uintptr_t)&ip6, sizeof (struct in6_addr)); /* * Check an IPv6 string will fit in scratch. */ size = INET6_ADDRSTRLEN; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } base = (char *)mstate->dtms_scratch_ptr; end = (char *)mstate->dtms_scratch_ptr + size - 1; *end-- = '\0'; /* * Find the longest run of 16 bit zero values * for the single allowed zero compression - "::". */ firstzero = -1; tryzero = -1; numzero = 1; for (i = 0; i < sizeof (struct in6_addr); i++) { #ifdef illumos if (ip6._S6_un._S6_u8[i] == 0 && #else if (ip6.__u6_addr.__u6_addr8[i] == 0 && #endif tryzero == -1 && i % 2 == 0) { tryzero = i; continue; } if (tryzero != -1 && #ifdef illumos (ip6._S6_un._S6_u8[i] != 0 || #else (ip6.__u6_addr.__u6_addr8[i] != 0 || #endif i == sizeof (struct in6_addr) - 1)) { if (i - tryzero <= numzero) { tryzero = -1; continue; } firstzero = tryzero; numzero = i - i % 2 - tryzero; tryzero = -1; #ifdef illumos if (ip6._S6_un._S6_u8[i] == 0 && #else if (ip6.__u6_addr.__u6_addr8[i] == 0 && #endif i == sizeof (struct in6_addr) - 1) numzero += 2; } } ASSERT(firstzero + numzero <= sizeof (struct in6_addr)); /* * Check for an IPv4 embedded address. */ v6end = sizeof (struct in6_addr) - 2; if (IN6_IS_ADDR_V4MAPPED(&ip6) || IN6_IS_ADDR_V4COMPAT(&ip6)) { for (i = sizeof (struct in6_addr) - 1; i >= DTRACE_V4MAPPED_OFFSET; i--) { ASSERT(end >= base); #ifdef illumos val = ip6._S6_un._S6_u8[i]; #else val = ip6.__u6_addr.__u6_addr8[i]; #endif if (val == 0) { *end-- = '0'; } else { for (; val; val /= 10) { *end-- = '0' + val % 10; } } if (i > DTRACE_V4MAPPED_OFFSET) *end-- = '.'; } if (subr == DIF_SUBR_INET_NTOA6) goto inetout; /* * Set v6end to skip the IPv4 address that * we have already stringified. */ v6end = 10; } /* * Build the IPv6 string by working through the * address in reverse. */ for (i = v6end; i >= 0; i -= 2) { ASSERT(end >= base); if (i == firstzero + numzero - 2) { *end-- = ':'; *end-- = ':'; i -= numzero - 2; continue; } if (i < 14 && i != firstzero - 2) *end-- = ':'; #ifdef illumos val = (ip6._S6_un._S6_u8[i] << 8) + ip6._S6_un._S6_u8[i + 1]; #else val = (ip6.__u6_addr.__u6_addr8[i] << 8) + ip6.__u6_addr.__u6_addr8[i + 1]; #endif if (val == 0) { *end-- = '0'; } else { for (; val; val /= 16) { *end-- = digits[val % 16]; } } } ASSERT(end + 1 >= base); } else { /* * The user didn't use AH_INET or AH_INET6. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); regs[rd] = 0; break; } inetout: regs[rd] = (uintptr_t)end + 1; mstate->dtms_scratch_ptr += size; break; } case DIF_SUBR_MEMREF: { uintptr_t size = 2 * sizeof(uintptr_t); uintptr_t *memref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t)); size_t scratch_size = ((uintptr_t) memref - mstate->dtms_scratch_ptr) + size; /* address and length */ memref[0] = tupregs[0].dttk_value; memref[1] = tupregs[1].dttk_value; regs[rd] = (uintptr_t) memref; mstate->dtms_scratch_ptr += scratch_size; break; } #ifndef illumos case DIF_SUBR_MEMSTR: { char *str = (char *)mstate->dtms_scratch_ptr; uintptr_t mem = tupregs[0].dttk_value; char c = tupregs[1].dttk_value; size_t size = tupregs[2].dttk_value; uint8_t n; int i; regs[rd] = 0; if (size == 0) break; if (!dtrace_canload(mem, size - 1, mstate, vstate)) break; if (!DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); break; } if (dtrace_memstr_max != 0 && size > dtrace_memstr_max) { *flags |= CPU_DTRACE_ILLOP; break; } for (i = 0; i < size - 1; i++) { n = dtrace_load8(mem++); str[i] = (n == 0) ? c : n; } str[size - 1] = 0; regs[rd] = (uintptr_t)str; mstate->dtms_scratch_ptr += size; break; } #endif case DIF_SUBR_TYPEREF: { uintptr_t size = 4 * sizeof(uintptr_t); uintptr_t *typeref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t)); size_t scratch_size = ((uintptr_t) typeref - mstate->dtms_scratch_ptr) + size; /* address, num_elements, type_str, type_len */ typeref[0] = tupregs[0].dttk_value; typeref[1] = tupregs[1].dttk_value; typeref[2] = tupregs[2].dttk_value; typeref[3] = tupregs[3].dttk_value; regs[rd] = (uintptr_t) typeref; mstate->dtms_scratch_ptr += scratch_size; break; } } } /* * Emulate the execution of DTrace IR instructions specified by the given * DIF object. This function is deliberately void of assertions as all of * the necessary checks are handled by a call to dtrace_difo_validate(). */ static uint64_t dtrace_dif_emulate(dtrace_difo_t *difo, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate, dtrace_state_t *state) { const dif_instr_t *text = difo->dtdo_buf; const uint_t textlen = difo->dtdo_len; const char *strtab = difo->dtdo_strtab; const uint64_t *inttab = difo->dtdo_inttab; uint64_t rval = 0; dtrace_statvar_t *svar; dtrace_dstate_t *dstate = &vstate->dtvs_dynvars; dtrace_difv_t *v; volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval; dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */ uint64_t regs[DIF_DIR_NREGS]; uint64_t *tmp; uint8_t cc_n = 0, cc_z = 0, cc_v = 0, cc_c = 0; int64_t cc_r; uint_t pc = 0, id, opc = 0; uint8_t ttop = 0; dif_instr_t instr; uint_t r1, r2, rd; /* * We stash the current DIF object into the machine state: we need it * for subsequent access checking. */ mstate->dtms_difo = difo; regs[DIF_REG_R0] = 0; /* %r0 is fixed at zero */ while (pc < textlen && !(*flags & CPU_DTRACE_FAULT)) { opc = pc; instr = text[pc++]; r1 = DIF_INSTR_R1(instr); r2 = DIF_INSTR_R2(instr); rd = DIF_INSTR_RD(instr); switch (DIF_INSTR_OP(instr)) { case DIF_OP_OR: regs[rd] = regs[r1] | regs[r2]; break; case DIF_OP_XOR: regs[rd] = regs[r1] ^ regs[r2]; break; case DIF_OP_AND: regs[rd] = regs[r1] & regs[r2]; break; case DIF_OP_SLL: regs[rd] = regs[r1] << regs[r2]; break; case DIF_OP_SRL: regs[rd] = regs[r1] >> regs[r2]; break; case DIF_OP_SUB: regs[rd] = regs[r1] - regs[r2]; break; case DIF_OP_ADD: regs[rd] = regs[r1] + regs[r2]; break; case DIF_OP_MUL: regs[rd] = regs[r1] * regs[r2]; break; case DIF_OP_SDIV: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = (int64_t)regs[r1] / (int64_t)regs[r2]; } break; case DIF_OP_UDIV: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = regs[r1] / regs[r2]; } break; case DIF_OP_SREM: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = (int64_t)regs[r1] % (int64_t)regs[r2]; } break; case DIF_OP_UREM: if (regs[r2] == 0) { regs[rd] = 0; *flags |= CPU_DTRACE_DIVZERO; } else { regs[rd] = regs[r1] % regs[r2]; } break; case DIF_OP_NOT: regs[rd] = ~regs[r1]; break; case DIF_OP_MOV: regs[rd] = regs[r1]; break; case DIF_OP_CMP: cc_r = regs[r1] - regs[r2]; cc_n = cc_r < 0; cc_z = cc_r == 0; cc_v = 0; cc_c = regs[r1] < regs[r2]; break; case DIF_OP_TST: cc_n = cc_v = cc_c = 0; cc_z = regs[r1] == 0; break; case DIF_OP_BA: pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BE: if (cc_z) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BNE: if (cc_z == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BG: if ((cc_z | (cc_n ^ cc_v)) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGU: if ((cc_c | cc_z) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGE: if ((cc_n ^ cc_v) == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BGEU: if (cc_c == 0) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BL: if (cc_n ^ cc_v) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLU: if (cc_c) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLE: if (cc_z | (cc_n ^ cc_v)) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_BLEU: if (cc_c | cc_z) pc = DIF_INSTR_LABEL(instr); break; case DIF_OP_RLDSB: if (!dtrace_canload(regs[r1], 1, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDSB: regs[rd] = (int8_t)dtrace_load8(regs[r1]); break; case DIF_OP_RLDSH: if (!dtrace_canload(regs[r1], 2, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDSH: regs[rd] = (int16_t)dtrace_load16(regs[r1]); break; case DIF_OP_RLDSW: if (!dtrace_canload(regs[r1], 4, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDSW: regs[rd] = (int32_t)dtrace_load32(regs[r1]); break; case DIF_OP_RLDUB: if (!dtrace_canload(regs[r1], 1, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDUB: regs[rd] = dtrace_load8(regs[r1]); break; case DIF_OP_RLDUH: if (!dtrace_canload(regs[r1], 2, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDUH: regs[rd] = dtrace_load16(regs[r1]); break; case DIF_OP_RLDUW: if (!dtrace_canload(regs[r1], 4, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDUW: regs[rd] = dtrace_load32(regs[r1]); break; case DIF_OP_RLDX: if (!dtrace_canload(regs[r1], 8, mstate, vstate)) break; /*FALLTHROUGH*/ case DIF_OP_LDX: regs[rd] = dtrace_load64(regs[r1]); break; case DIF_OP_ULDSB: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int8_t) dtrace_fuword8((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDSH: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int16_t) dtrace_fuword16((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDSW: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = (int32_t) dtrace_fuword32((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUB: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword8((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUH: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword16((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDUW: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword32((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_ULDX: DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); regs[rd] = dtrace_fuword64((void *)(uintptr_t)regs[r1]); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); break; case DIF_OP_RET: rval = regs[rd]; pc = textlen; break; case DIF_OP_NOP: break; case DIF_OP_SETX: regs[rd] = inttab[DIF_INSTR_INTEGER(instr)]; break; case DIF_OP_SETS: regs[rd] = (uint64_t)(uintptr_t) (strtab + DIF_INSTR_STRING(instr)); break; case DIF_OP_SCMP: { size_t sz = state->dts_options[DTRACEOPT_STRSIZE]; uintptr_t s1 = regs[r1]; uintptr_t s2 = regs[r2]; if (s1 != 0 && !dtrace_strcanload(s1, sz, mstate, vstate)) break; if (s2 != 0 && !dtrace_strcanload(s2, sz, mstate, vstate)) break; cc_r = dtrace_strncmp((char *)s1, (char *)s2, sz); cc_n = cc_r < 0; cc_z = cc_r == 0; cc_v = cc_c = 0; break; } case DIF_OP_LDGA: regs[rd] = dtrace_dif_variable(mstate, state, r1, regs[r2]); break; case DIF_OP_LDGS: id = DIF_INSTR_VAR(instr); if (id >= DIF_VAR_OTHER_UBASE) { uintptr_t a; id -= DIF_VAR_OTHER_UBASE; svar = vstate->dtvs_globals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (!(v->dtdv_type.dtdt_flags & DIF_TF_BYREF)) { regs[rd] = svar->dtsv_data; break; } a = (uintptr_t)svar->dtsv_data; if (*(uint8_t *)a == UINT8_MAX) { /* * If the 0th byte is set to UINT8_MAX * then this is to be treated as a * reference to a NULL variable. */ regs[rd] = 0; } else { regs[rd] = a + sizeof (uint64_t); } break; } regs[rd] = dtrace_dif_variable(mstate, state, id, 0); break; case DIF_OP_STGS: id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; svar = vstate->dtvs_globals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; ASSERT(a != 0); ASSERT(svar->dtsv_size != 0); if (regs[rd] == 0) { *(uint8_t *)a = UINT8_MAX; break; } else { *(uint8_t *)a = 0; a += sizeof (uint64_t); } if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); break; } svar->dtsv_data = regs[rd]; break; case DIF_OP_LDTA: /* * There are no DTrace built-in thread-local arrays at * present. This opcode is saved for future work. */ *flags |= CPU_DTRACE_ILLOP; regs[rd] = 0; break; case DIF_OP_LDLS: id = DIF_INSTR_VAR(instr); if (id < DIF_VAR_OTHER_UBASE) { /* * For now, this has no meaning. */ regs[rd] = 0; break; } id -= DIF_VAR_OTHER_UBASE; ASSERT(id < vstate->dtvs_nlocals); ASSERT(vstate->dtvs_locals != NULL); svar = vstate->dtvs_locals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; size_t sz = v->dtdv_type.dtdt_size; sz += sizeof (uint64_t); ASSERT(svar->dtsv_size == NCPU * sz); a += curcpu * sz; if (*(uint8_t *)a == UINT8_MAX) { /* * If the 0th byte is set to UINT8_MAX * then this is to be treated as a * reference to a NULL variable. */ regs[rd] = 0; } else { regs[rd] = a + sizeof (uint64_t); } break; } ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t)); tmp = (uint64_t *)(uintptr_t)svar->dtsv_data; regs[rd] = tmp[curcpu]; break; case DIF_OP_STLS: id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; ASSERT(id < vstate->dtvs_nlocals); ASSERT(vstate->dtvs_locals != NULL); svar = vstate->dtvs_locals[id]; ASSERT(svar != NULL); v = &svar->dtsv_var; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { uintptr_t a = (uintptr_t)svar->dtsv_data; size_t sz = v->dtdv_type.dtdt_size; sz += sizeof (uint64_t); ASSERT(svar->dtsv_size == NCPU * sz); a += curcpu * sz; if (regs[rd] == 0) { *(uint8_t *)a = UINT8_MAX; break; } else { *(uint8_t *)a = 0; a += sizeof (uint64_t); } if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], (void *)a, &v->dtdv_type); break; } ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t)); tmp = (uint64_t *)(uintptr_t)svar->dtsv_data; tmp[curcpu] = regs[rd]; break; case DIF_OP_LDTS: { dtrace_dynvar_t *dvar; dtrace_key_t *key; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; v = &vstate->dtvs_tlocals[id]; key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_value = (uint64_t)id; key[0].dttk_size = 0; DTRACE_TLS_THRKEY(key[1].dttk_value); key[1].dttk_size = 0; dvar = dtrace_dynvar(dstate, 2, key, sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC, mstate, vstate); if (dvar == NULL) { regs[rd] = 0; break; } if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data; } else { regs[rd] = *((uint64_t *)dvar->dtdv_data); } break; } case DIF_OP_STTS: { dtrace_dynvar_t *dvar; dtrace_key_t *key; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_value = (uint64_t)id; key[0].dttk_size = 0; DTRACE_TLS_THRKEY(key[1].dttk_value); key[1].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; dvar = dtrace_dynvar(dstate, 2, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : DTRACE_DYNVAR_DEALLOC, mstate, vstate); /* * Given that we're storing to thread-local data, * we need to flush our predicate cache. */ curthread->t_predcache = 0; if (dvar == NULL) break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { *((uint64_t *)dvar->dtdv_data) = regs[rd]; } break; } case DIF_OP_SRA: regs[rd] = (int64_t)regs[r1] >> regs[r2]; break; case DIF_OP_CALL: dtrace_dif_subr(DIF_INSTR_SUBR(instr), rd, regs, tupregs, ttop, mstate, state); break; case DIF_OP_PUSHTR: if (ttop == DIF_DTR_NREGS) { *flags |= CPU_DTRACE_TUPOFLOW; break; } if (r1 == DIF_TYPE_STRING) { /* * If this is a string type and the size is 0, * we'll use the system-wide default string * size. Note that we are _not_ looking at * the value of the DTRACEOPT_STRSIZE option; * had this been set, we would expect to have * a non-zero size value in the "pushtr". */ tupregs[ttop].dttk_size = dtrace_strlen((char *)(uintptr_t)regs[rd], regs[r2] ? regs[r2] : dtrace_strsize_default) + 1; } else { tupregs[ttop].dttk_size = regs[r2]; } tupregs[ttop++].dttk_value = regs[rd]; break; case DIF_OP_PUSHTV: if (ttop == DIF_DTR_NREGS) { *flags |= CPU_DTRACE_TUPOFLOW; break; } tupregs[ttop].dttk_value = regs[rd]; tupregs[ttop++].dttk_size = 0; break; case DIF_OP_POPTS: if (ttop != 0) ttop--; break; case DIF_OP_FLUSHTS: ttop = 0; break; case DIF_OP_LDGAA: case DIF_OP_LDTAA: { dtrace_dynvar_t *dvar; dtrace_key_t *key = tupregs; uint_t nkeys = ttop; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key[nkeys].dttk_value = (uint64_t)id; key[nkeys++].dttk_size = 0; if (DIF_INSTR_OP(instr) == DIF_OP_LDTAA) { DTRACE_TLS_THRKEY(key[nkeys].dttk_value); key[nkeys++].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; } else { v = &vstate->dtvs_globals[id]->dtsv_var; } dvar = dtrace_dynvar(dstate, nkeys, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC, mstate, vstate); if (dvar == NULL) { regs[rd] = 0; break; } if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data; } else { regs[rd] = *((uint64_t *)dvar->dtdv_data); } break; } case DIF_OP_STGAA: case DIF_OP_STTAA: { dtrace_dynvar_t *dvar; dtrace_key_t *key = tupregs; uint_t nkeys = ttop; id = DIF_INSTR_VAR(instr); ASSERT(id >= DIF_VAR_OTHER_UBASE); id -= DIF_VAR_OTHER_UBASE; key[nkeys].dttk_value = (uint64_t)id; key[nkeys++].dttk_size = 0; if (DIF_INSTR_OP(instr) == DIF_OP_STTAA) { DTRACE_TLS_THRKEY(key[nkeys].dttk_value); key[nkeys++].dttk_size = 0; v = &vstate->dtvs_tlocals[id]; } else { v = &vstate->dtvs_globals[id]->dtsv_var; } dvar = dtrace_dynvar(dstate, nkeys, key, v->dtdv_type.dtdt_size > sizeof (uint64_t) ? v->dtdv_type.dtdt_size : sizeof (uint64_t), regs[rd] ? DTRACE_DYNVAR_ALLOC : DTRACE_DYNVAR_DEALLOC, mstate, vstate); if (dvar == NULL) break; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) { if (!dtrace_vcanload( (void *)(uintptr_t)regs[rd], &v->dtdv_type, mstate, vstate)) break; dtrace_vcopy((void *)(uintptr_t)regs[rd], dvar->dtdv_data, &v->dtdv_type); } else { *((uint64_t *)dvar->dtdv_data) = regs[rd]; } break; } case DIF_OP_ALLOCS: { uintptr_t ptr = P2ROUNDUP(mstate->dtms_scratch_ptr, 8); size_t size = ptr - mstate->dtms_scratch_ptr + regs[r1]; /* * Rounding up the user allocation size could have * overflowed large, bogus allocations (like -1ULL) to * 0. */ if (size < regs[r1] || !DTRACE_INSCRATCH(mstate, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); regs[rd] = 0; break; } dtrace_bzero((void *) mstate->dtms_scratch_ptr, size); mstate->dtms_scratch_ptr += size; regs[rd] = ptr; break; } case DIF_OP_COPYS: if (!dtrace_canstore(regs[rd], regs[r2], mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (!dtrace_canload(regs[r1], regs[r2], mstate, vstate)) break; dtrace_bcopy((void *)(uintptr_t)regs[r1], (void *)(uintptr_t)regs[rd], (size_t)regs[r2]); break; case DIF_OP_STB: if (!dtrace_canstore(regs[rd], 1, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } *((uint8_t *)(uintptr_t)regs[rd]) = (uint8_t)regs[r1]; break; case DIF_OP_STH: if (!dtrace_canstore(regs[rd], 2, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 1) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint16_t *)(uintptr_t)regs[rd]) = (uint16_t)regs[r1]; break; case DIF_OP_STW: if (!dtrace_canstore(regs[rd], 4, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 3) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint32_t *)(uintptr_t)regs[rd]) = (uint32_t)regs[r1]; break; case DIF_OP_STX: if (!dtrace_canstore(regs[rd], 8, mstate, vstate)) { *flags |= CPU_DTRACE_BADADDR; *illval = regs[rd]; break; } if (regs[rd] & 7) { *flags |= CPU_DTRACE_BADALIGN; *illval = regs[rd]; break; } *((uint64_t *)(uintptr_t)regs[rd]) = regs[r1]; break; } } if (!(*flags & CPU_DTRACE_FAULT)) return (rval); mstate->dtms_fltoffs = opc * sizeof (dif_instr_t); mstate->dtms_present |= DTRACE_MSTATE_FLTOFFS; return (0); } static void dtrace_action_breakpoint(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; dtrace_provider_t *prov = probe->dtpr_provider; char c[DTRACE_FULLNAMELEN + 80], *str; char *msg = "dtrace: breakpoint action at probe "; char *ecbmsg = " (ecb "; uintptr_t mask = (0xf << (sizeof (uintptr_t) * NBBY / 4)); uintptr_t val = (uintptr_t)ecb; int shift = (sizeof (uintptr_t) * NBBY) - 4, i = 0; if (dtrace_destructive_disallow) return; /* * It's impossible to be taking action on the NULL probe. */ ASSERT(probe != NULL); /* * This is a poor man's (destitute man's?) sprintf(): we want to * print the provider name, module name, function name and name of * the probe, along with the hex address of the ECB with the breakpoint * action -- all of which we must place in the character buffer by * hand. */ while (*msg != '\0') c[i++] = *msg++; for (str = prov->dtpv_name; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_mod; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_func; *str != '\0'; str++) c[i++] = *str; c[i++] = ':'; for (str = probe->dtpr_name; *str != '\0'; str++) c[i++] = *str; while (*ecbmsg != '\0') c[i++] = *ecbmsg++; while (shift >= 0) { mask = (uintptr_t)0xf << shift; if (val >= ((uintptr_t)1 << shift)) c[i++] = "0123456789abcdef"[(val & mask) >> shift]; shift -= 4; } c[i++] = ')'; c[i] = '\0'; #ifdef illumos debug_enter(c); #else kdb_enter(KDB_WHY_DTRACE, "breakpoint action"); #endif } static void dtrace_action_panic(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; /* * It's impossible to be taking action on the NULL probe. */ ASSERT(probe != NULL); if (dtrace_destructive_disallow) return; if (dtrace_panicked != NULL) return; if (dtrace_casptr(&dtrace_panicked, NULL, curthread) != NULL) return; /* * We won the right to panic. (We want to be sure that only one * thread calls panic() from dtrace_probe(), and that panic() is * called exactly once.) */ dtrace_panic("dtrace: panic action at probe %s:%s:%s:%s (ecb %p)", probe->dtpr_provider->dtpv_name, probe->dtpr_mod, probe->dtpr_func, probe->dtpr_name, (void *)ecb); } static void dtrace_action_raise(uint64_t sig) { if (dtrace_destructive_disallow) return; if (sig >= NSIG) { DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return; } #ifdef illumos /* * raise() has a queue depth of 1 -- we ignore all subsequent * invocations of the raise() action. */ if (curthread->t_dtrace_sig == 0) curthread->t_dtrace_sig = (uint8_t)sig; curthread->t_sig_check = 1; aston(curthread); #else struct proc *p = curproc; PROC_LOCK(p); kern_psignal(p, sig); PROC_UNLOCK(p); #endif } static void dtrace_action_stop(void) { if (dtrace_destructive_disallow) return; #ifdef illumos if (!curthread->t_dtrace_stop) { curthread->t_dtrace_stop = 1; curthread->t_sig_check = 1; aston(curthread); } #else struct proc *p = curproc; PROC_LOCK(p); kern_psignal(p, SIGSTOP); PROC_UNLOCK(p); #endif } static void dtrace_action_chill(dtrace_mstate_t *mstate, hrtime_t val) { hrtime_t now; volatile uint16_t *flags; #ifdef illumos cpu_t *cpu = CPU; #else cpu_t *cpu = &solaris_cpu[curcpu]; #endif if (dtrace_destructive_disallow) return; flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; now = dtrace_gethrtime(); if (now - cpu->cpu_dtrace_chillmark > dtrace_chill_interval) { /* * We need to advance the mark to the current time. */ cpu->cpu_dtrace_chillmark = now; cpu->cpu_dtrace_chilled = 0; } /* * Now check to see if the requested chill time would take us over * the maximum amount of time allowed in the chill interval. (Or * worse, if the calculation itself induces overflow.) */ if (cpu->cpu_dtrace_chilled + val > dtrace_chill_max || cpu->cpu_dtrace_chilled + val < cpu->cpu_dtrace_chilled) { *flags |= CPU_DTRACE_ILLOP; return; } while (dtrace_gethrtime() - now < val) continue; /* * Normally, we assure that the value of the variable "timestamp" does * not change within an ECB. The presence of chill() represents an * exception to this rule, however. */ mstate->dtms_present &= ~DTRACE_MSTATE_TIMESTAMP; cpu->cpu_dtrace_chilled += val; } static void dtrace_action_ustack(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t *buf, uint64_t arg) { int nframes = DTRACE_USTACK_NFRAMES(arg); int strsize = DTRACE_USTACK_STRSIZE(arg); uint64_t *pcs = &buf[1], *fps; char *str = (char *)&pcs[nframes]; int size, offs = 0, i, j; uintptr_t old = mstate->dtms_scratch_ptr, saved; uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; char *sym; /* * Should be taking a faster path if string space has not been * allocated. */ ASSERT(strsize != 0); /* * We will first allocate some temporary space for the frame pointers. */ fps = (uint64_t *)P2ROUNDUP(mstate->dtms_scratch_ptr, 8); size = (uintptr_t)fps - mstate->dtms_scratch_ptr + (nframes * sizeof (uint64_t)); if (!DTRACE_INSCRATCH(mstate, size)) { /* * Not enough room for our frame pointers -- need to indicate * that we ran out of scratch space. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH); return; } mstate->dtms_scratch_ptr += size; saved = mstate->dtms_scratch_ptr; /* * Now get a stack with both program counters and frame pointers. */ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getufpstack(buf, fps, nframes + 1); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); /* * If that faulted, we're cooked. */ if (*flags & CPU_DTRACE_FAULT) goto out; /* * Now we want to walk up the stack, calling the USTACK helper. For * each iteration, we restore the scratch pointer. */ for (i = 0; i < nframes; i++) { mstate->dtms_scratch_ptr = saved; if (offs >= strsize) break; sym = (char *)(uintptr_t)dtrace_helper( DTRACE_HELPER_ACTION_USTACK, mstate, state, pcs[i], fps[i]); /* * If we faulted while running the helper, we're going to * clear the fault and null out the corresponding string. */ if (*flags & CPU_DTRACE_FAULT) { *flags &= ~CPU_DTRACE_FAULT; str[offs++] = '\0'; continue; } if (sym == NULL) { str[offs++] = '\0'; continue; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); /* * Now copy in the string that the helper returned to us. */ for (j = 0; offs + j < strsize; j++) { if ((str[offs + j] = sym[j]) == '\0') break; } DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); offs += j + 1; } if (offs >= strsize) { /* * If we didn't have room for all of the strings, we don't * abort processing -- this needn't be a fatal error -- but we * still want to increment a counter (dts_stkstroverflows) to * allow this condition to be warned about. (If this is from * a jstack() action, it is easily tuned via jstackstrsize.) */ dtrace_error(&state->dts_stkstroverflows); } while (offs < strsize) str[offs++] = '\0'; out: mstate->dtms_scratch_ptr = old; } static void dtrace_store_by_ref(dtrace_difo_t *dp, caddr_t tomax, size_t size, size_t *valoffsp, uint64_t *valp, uint64_t end, int intuple, int dtkind) { volatile uint16_t *flags; uint64_t val = *valp; size_t valoffs = *valoffsp; flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; ASSERT(dtkind == DIF_TF_BYREF || dtkind == DIF_TF_BYUREF); /* * If this is a string, we're going to only load until we find the zero * byte -- after which we'll store zero bytes. */ if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) { char c = '\0' + 1; size_t s; for (s = 0; s < size; s++) { if (c != '\0' && dtkind == DIF_TF_BYREF) { c = dtrace_load8(val++); } else if (c != '\0' && dtkind == DIF_TF_BYUREF) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); c = dtrace_fuword8((void *)(uintptr_t)val++); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); if (*flags & CPU_DTRACE_FAULT) break; } DTRACE_STORE(uint8_t, tomax, valoffs++, c); if (c == '\0' && intuple) break; } } else { uint8_t c; while (valoffs < end) { if (dtkind == DIF_TF_BYREF) { c = dtrace_load8(val++); } else if (dtkind == DIF_TF_BYUREF) { DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); c = dtrace_fuword8((void *)(uintptr_t)val++); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); if (*flags & CPU_DTRACE_FAULT) break; } DTRACE_STORE(uint8_t, tomax, valoffs++, c); } } *valp = val; *valoffsp = valoffs; } /* * If you're looking for the epicenter of DTrace, you just found it. This * is the function called by the provider to fire a probe -- from which all * subsequent probe-context DTrace activity emanates. */ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) { processorid_t cpuid; dtrace_icookie_t cookie; dtrace_probe_t *probe; dtrace_mstate_t mstate; dtrace_ecb_t *ecb; dtrace_action_t *act; intptr_t offs; size_t size; int vtime, onintr; volatile uint16_t *flags; hrtime_t now; if (panicstr != NULL) return; #ifdef illumos /* * Kick out immediately if this CPU is still being born (in which case * curthread will be set to -1) or the current thread can't allow * probes in its current context. */ if (((uintptr_t)curthread & 1) || (curthread->t_flag & T_DONTDTRACE)) return; #endif cookie = dtrace_interrupt_disable(); probe = dtrace_probes[id - 1]; cpuid = curcpu; onintr = CPU_ON_INTR(CPU); if (!onintr && probe->dtpr_predcache != DTRACE_CACHEIDNONE && probe->dtpr_predcache == curthread->t_predcache) { /* * We have hit in the predicate cache; we know that * this predicate would evaluate to be false. */ dtrace_interrupt_enable(cookie); return; } #ifdef illumos if (panic_quiesce) { #else if (panicstr != NULL) { #endif /* * We don't trace anything if we're panicking. */ dtrace_interrupt_enable(cookie); return; } - now = dtrace_gethrtime(); + now = mstate.dtms_timestamp = dtrace_gethrtime(); + mstate.dtms_present |= DTRACE_MSTATE_TIMESTAMP; vtime = dtrace_vtime_references != 0; if (vtime && curthread->t_dtrace_start) curthread->t_dtrace_vtime += now - curthread->t_dtrace_start; mstate.dtms_difo = NULL; mstate.dtms_probe = probe; mstate.dtms_strtok = 0; mstate.dtms_arg[0] = arg0; mstate.dtms_arg[1] = arg1; mstate.dtms_arg[2] = arg2; mstate.dtms_arg[3] = arg3; mstate.dtms_arg[4] = arg4; flags = (volatile uint16_t *)&cpu_core[cpuid].cpuc_dtrace_flags; for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) { dtrace_predicate_t *pred = ecb->dte_predicate; dtrace_state_t *state = ecb->dte_state; dtrace_buffer_t *buf = &state->dts_buffer[cpuid]; dtrace_buffer_t *aggbuf = &state->dts_aggbuffer[cpuid]; dtrace_vstate_t *vstate = &state->dts_vstate; dtrace_provider_t *prov = probe->dtpr_provider; uint64_t tracememsize = 0; int committed = 0; caddr_t tomax; /* * A little subtlety with the following (seemingly innocuous) * declaration of the automatic 'val': by looking at the * code, you might think that it could be declared in the * action processing loop, below. (That is, it's only used in * the action processing loop.) However, it must be declared * out of that scope because in the case of DIF expression * arguments to aggregating actions, one iteration of the * action loop will use the last iteration's value. */ uint64_t val = 0; mstate.dtms_present = DTRACE_MSTATE_ARGS | DTRACE_MSTATE_PROBE; mstate.dtms_getf = NULL; *flags &= ~CPU_DTRACE_ERROR; if (prov == dtrace_provider) { /* * If dtrace itself is the provider of this probe, * we're only going to continue processing the ECB if * arg0 (the dtrace_state_t) is equal to the ECB's * creating state. (This prevents disjoint consumers * from seeing one another's metaprobes.) */ if (arg0 != (uint64_t)(uintptr_t)state) continue; } if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE) { /* * We're not currently active. If our provider isn't * the dtrace pseudo provider, we're not interested. */ if (prov != dtrace_provider) continue; /* * Now we must further check if we are in the BEGIN * probe. If we are, we will only continue processing * if we're still in WARMUP -- if one BEGIN enabling * has invoked the exit() action, we don't want to * evaluate subsequent BEGIN enablings. */ if (probe->dtpr_id == dtrace_probeid_begin && state->dts_activity != DTRACE_ACTIVITY_WARMUP) { ASSERT(state->dts_activity == DTRACE_ACTIVITY_DRAINING); continue; } } if (ecb->dte_cond) { /* * If the dte_cond bits indicate that this * consumer is only allowed to see user-mode firings * of this probe, call the provider's dtps_usermode() * entry point to check that the probe was fired * while in a user context. Skip this ECB if that's * not the case. */ if ((ecb->dte_cond & DTRACE_COND_USERMODE) && prov->dtpv_pops.dtps_usermode(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg) == 0) continue; #ifdef illumos /* * This is more subtle than it looks. We have to be * absolutely certain that CRED() isn't going to * change out from under us so it's only legit to * examine that structure if we're in constrained * situations. Currently, the only times we'll this * check is if a non-super-user has enabled the * profile or syscall providers -- providers that * allow visibility of all processes. For the * profile case, the check above will ensure that * we're examining a user context. */ if (ecb->dte_cond & DTRACE_COND_OWNER) { cred_t *cr; cred_t *s_cr = ecb->dte_state->dts_cred.dcr_cred; proc_t *proc; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_uid != cr->cr_uid || s_cr->cr_uid != cr->cr_ruid || s_cr->cr_uid != cr->cr_suid || s_cr->cr_gid != cr->cr_gid || s_cr->cr_gid != cr->cr_rgid || s_cr->cr_gid != cr->cr_sgid || (proc = ttoproc(curthread)) == NULL || (proc->p_flag & SNOCD)) continue; } if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) { cred_t *cr; cred_t *s_cr = ecb->dte_state->dts_cred.dcr_cred; ASSERT(s_cr != NULL); if ((cr = CRED()) == NULL || s_cr->cr_zone->zone_id != cr->cr_zone->zone_id) continue; } #endif } if (now - state->dts_alive > dtrace_deadman_timeout) { /* * We seem to be dead. Unless we (a) have kernel * destructive permissions (b) have explicitly enabled * destructive actions and (c) destructive actions have * not been disabled, we're going to transition into * the KILLED state, from which no further processing * on this state will be performed. */ if (!dtrace_priv_kernel_destructive(state) || !state->dts_cred.dcr_destructive || dtrace_destructive_disallow) { void *activity = &state->dts_activity; dtrace_activity_t current; do { current = state->dts_activity; } while (dtrace_cas32(activity, current, DTRACE_ACTIVITY_KILLED) != current); continue; } } if ((offs = dtrace_buffer_reserve(buf, ecb->dte_needed, ecb->dte_alignment, state, &mstate)) < 0) continue; tomax = buf->dtb_tomax; ASSERT(tomax != NULL); if (ecb->dte_size != 0) { dtrace_rechdr_t dtrh; if (!(mstate.dtms_present & DTRACE_MSTATE_TIMESTAMP)) { mstate.dtms_timestamp = dtrace_gethrtime(); mstate.dtms_present |= DTRACE_MSTATE_TIMESTAMP; } ASSERT3U(ecb->dte_size, >=, sizeof (dtrace_rechdr_t)); dtrh.dtrh_epid = ecb->dte_epid; DTRACE_RECORD_STORE_TIMESTAMP(&dtrh, mstate.dtms_timestamp); *((dtrace_rechdr_t *)(tomax + offs)) = dtrh; } mstate.dtms_epid = ecb->dte_epid; mstate.dtms_present |= DTRACE_MSTATE_EPID; if (state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) mstate.dtms_access = DTRACE_ACCESS_KERNEL; else mstate.dtms_access = 0; if (pred != NULL) { dtrace_difo_t *dp = pred->dtp_difo; int rval; rval = dtrace_dif_emulate(dp, &mstate, vstate, state); if (!(*flags & CPU_DTRACE_ERROR) && !rval) { dtrace_cacheid_t cid = probe->dtpr_predcache; if (cid != DTRACE_CACHEIDNONE && !onintr) { /* * Update the predicate cache... */ ASSERT(cid == pred->dtp_cacheid); curthread->t_predcache = cid; } continue; } } for (act = ecb->dte_action; !(*flags & CPU_DTRACE_ERROR) && act != NULL; act = act->dta_next) { size_t valoffs; dtrace_difo_t *dp; dtrace_recdesc_t *rec = &act->dta_rec; size = rec->dtrd_size; valoffs = offs + rec->dtrd_offset; if (DTRACEACT_ISAGG(act->dta_kind)) { uint64_t v = 0xbad; dtrace_aggregation_t *agg; agg = (dtrace_aggregation_t *)act; if ((dp = act->dta_difo) != NULL) v = dtrace_dif_emulate(dp, &mstate, vstate, state); if (*flags & CPU_DTRACE_ERROR) continue; /* * Note that we always pass the expression * value from the previous iteration of the * action loop. This value will only be used * if there is an expression argument to the * aggregating action, denoted by the * dtag_hasarg field. */ dtrace_aggregate(agg, buf, offs, aggbuf, v, val); continue; } switch (act->dta_kind) { case DTRACEACT_STOP: if (dtrace_priv_proc_destructive(state)) dtrace_action_stop(); continue; case DTRACEACT_BREAKPOINT: if (dtrace_priv_kernel_destructive(state)) dtrace_action_breakpoint(ecb); continue; case DTRACEACT_PANIC: if (dtrace_priv_kernel_destructive(state)) dtrace_action_panic(ecb); continue; case DTRACEACT_STACK: if (!dtrace_priv_kernel(state)) continue; dtrace_getpcstack((pc_t *)(tomax + valoffs), size / sizeof (pc_t), probe->dtpr_aframes, DTRACE_ANCHORED(probe) ? NULL : (uint32_t *)arg0); continue; case DTRACEACT_JSTACK: case DTRACEACT_USTACK: if (!dtrace_priv_proc(state)) continue; /* * See comment in DIF_VAR_PID. */ if (DTRACE_ANCHORED(mstate.dtms_probe) && CPU_ON_INTR(CPU)) { int depth = DTRACE_USTACK_NFRAMES( rec->dtrd_arg) + 1; dtrace_bzero((void *)(tomax + valoffs), DTRACE_USTACK_STRSIZE(rec->dtrd_arg) + depth * sizeof (uint64_t)); continue; } if (DTRACE_USTACK_STRSIZE(rec->dtrd_arg) != 0 && curproc->p_dtrace_helpers != NULL) { /* * This is the slow path -- we have * allocated string space, and we're * getting the stack of a process that * has helpers. Call into a separate * routine to perform this processing. */ dtrace_action_ustack(&mstate, state, (uint64_t *)(tomax + valoffs), rec->dtrd_arg); continue; } DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); dtrace_getupcstack((uint64_t *) (tomax + valoffs), DTRACE_USTACK_NFRAMES(rec->dtrd_arg) + 1); DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); continue; default: break; } dp = act->dta_difo; ASSERT(dp != NULL); val = dtrace_dif_emulate(dp, &mstate, vstate, state); if (*flags & CPU_DTRACE_ERROR) continue; switch (act->dta_kind) { case DTRACEACT_SPECULATE: { dtrace_rechdr_t *dtrh; ASSERT(buf == &state->dts_buffer[cpuid]); buf = dtrace_speculation_buffer(state, cpuid, val); if (buf == NULL) { *flags |= CPU_DTRACE_DROP; continue; } offs = dtrace_buffer_reserve(buf, ecb->dte_needed, ecb->dte_alignment, state, NULL); if (offs < 0) { *flags |= CPU_DTRACE_DROP; continue; } tomax = buf->dtb_tomax; ASSERT(tomax != NULL); if (ecb->dte_size == 0) continue; ASSERT3U(ecb->dte_size, >=, sizeof (dtrace_rechdr_t)); dtrh = ((void *)(tomax + offs)); dtrh->dtrh_epid = ecb->dte_epid; /* * When the speculation is committed, all of * the records in the speculative buffer will * have their timestamps set to the commit * time. Until then, it is set to a sentinel * value, for debugability. */ DTRACE_RECORD_STORE_TIMESTAMP(dtrh, UINT64_MAX); continue; } case DTRACEACT_PRINTM: { /* The DIF returns a 'memref'. */ uintptr_t *memref = (uintptr_t *)(uintptr_t) val; /* Get the size from the memref. */ size = memref[1]; /* * Check if the size exceeds the allocated * buffer size. */ if (size + sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) { /* Flag a drop! */ *flags |= CPU_DTRACE_DROP; continue; } /* Store the size in the buffer first. */ DTRACE_STORE(uintptr_t, tomax, valoffs, size); /* * Offset the buffer address to the start * of the data. */ valoffs += sizeof(uintptr_t); /* * Reset to the memory address rather than * the memref array, then let the BYREF * code below do the work to store the * memory data in the buffer. */ val = memref[0]; break; } case DTRACEACT_PRINTT: { /* The DIF returns a 'typeref'. */ uintptr_t *typeref = (uintptr_t *)(uintptr_t) val; char c = '\0' + 1; size_t s; /* * Get the type string length and round it * up so that the data that follows is * aligned for easy access. */ size_t typs = strlen((char *) typeref[2]) + 1; typs = roundup(typs, sizeof(uintptr_t)); /* *Get the size from the typeref using the * number of elements and the type size. */ size = typeref[1] * typeref[3]; /* * Check if the size exceeds the allocated * buffer size. */ if (size + typs + 2 * sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) { /* Flag a drop! */ *flags |= CPU_DTRACE_DROP; } /* Store the size in the buffer first. */ DTRACE_STORE(uintptr_t, tomax, valoffs, size); valoffs += sizeof(uintptr_t); /* Store the type size in the buffer. */ DTRACE_STORE(uintptr_t, tomax, valoffs, typeref[3]); valoffs += sizeof(uintptr_t); val = typeref[2]; for (s = 0; s < typs; s++) { if (c != '\0') c = dtrace_load8(val++); DTRACE_STORE(uint8_t, tomax, valoffs++, c); } /* * Reset to the memory address rather than * the typeref array, then let the BYREF * code below do the work to store the * memory data in the buffer. */ val = typeref[0]; break; } case DTRACEACT_CHILL: if (dtrace_priv_kernel_destructive(state)) dtrace_action_chill(&mstate, val); continue; case DTRACEACT_RAISE: if (dtrace_priv_proc_destructive(state)) dtrace_action_raise(val); continue; case DTRACEACT_COMMIT: ASSERT(!committed); /* * We need to commit our buffer state. */ if (ecb->dte_size) buf->dtb_offset = offs + ecb->dte_size; buf = &state->dts_buffer[cpuid]; dtrace_speculation_commit(state, cpuid, val); committed = 1; continue; case DTRACEACT_DISCARD: dtrace_speculation_discard(state, cpuid, val); continue; case DTRACEACT_DIFEXPR: case DTRACEACT_LIBACT: case DTRACEACT_PRINTF: case DTRACEACT_PRINTA: case DTRACEACT_SYSTEM: case DTRACEACT_FREOPEN: case DTRACEACT_TRACEMEM: break; case DTRACEACT_TRACEMEM_DYNSIZE: tracememsize = val; break; case DTRACEACT_SYM: case DTRACEACT_MOD: if (!dtrace_priv_kernel(state)) continue; break; case DTRACEACT_USYM: case DTRACEACT_UMOD: case DTRACEACT_UADDR: { #ifdef illumos struct pid *pid = curthread->t_procp->p_pidp; #endif if (!dtrace_priv_proc(state)) continue; DTRACE_STORE(uint64_t, tomax, #ifdef illumos valoffs, (uint64_t)pid->pid_id); #else valoffs, (uint64_t) curproc->p_pid); #endif DTRACE_STORE(uint64_t, tomax, valoffs + sizeof (uint64_t), val); continue; } case DTRACEACT_EXIT: { /* * For the exit action, we are going to attempt * to atomically set our activity to be * draining. If this fails (either because * another CPU has beat us to the exit action, * or because our current activity is something * other than ACTIVE or WARMUP), we will * continue. This assures that the exit action * can be successfully recorded at most once * when we're in the ACTIVE state. If we're * encountering the exit() action while in * COOLDOWN, however, we want to honor the new * status code. (We know that we're the only * thread in COOLDOWN, so there is no race.) */ void *activity = &state->dts_activity; dtrace_activity_t current = state->dts_activity; if (current == DTRACE_ACTIVITY_COOLDOWN) break; if (current != DTRACE_ACTIVITY_WARMUP) current = DTRACE_ACTIVITY_ACTIVE; if (dtrace_cas32(activity, current, DTRACE_ACTIVITY_DRAINING) != current) { *flags |= CPU_DTRACE_DROP; continue; } break; } default: ASSERT(0); } if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF || dp->dtdo_rtype.dtdt_flags & DIF_TF_BYUREF) { uintptr_t end = valoffs + size; if (tracememsize != 0 && valoffs + tracememsize < end) { end = valoffs + tracememsize; tracememsize = 0; } if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF && !dtrace_vcanload((void *)(uintptr_t)val, &dp->dtdo_rtype, &mstate, vstate)) continue; dtrace_store_by_ref(dp, tomax, size, &valoffs, &val, end, act->dta_intuple, dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF ? DIF_TF_BYREF: DIF_TF_BYUREF); continue; } switch (size) { case 0: break; case sizeof (uint8_t): DTRACE_STORE(uint8_t, tomax, valoffs, val); break; case sizeof (uint16_t): DTRACE_STORE(uint16_t, tomax, valoffs, val); break; case sizeof (uint32_t): DTRACE_STORE(uint32_t, tomax, valoffs, val); break; case sizeof (uint64_t): DTRACE_STORE(uint64_t, tomax, valoffs, val); break; default: /* * Any other size should have been returned by * reference, not by value. */ ASSERT(0); break; } } if (*flags & CPU_DTRACE_DROP) continue; if (*flags & CPU_DTRACE_FAULT) { int ndx; dtrace_action_t *err; buf->dtb_errors++; if (probe->dtpr_id == dtrace_probeid_error) { /* * There's nothing we can do -- we had an * error on the error probe. We bump an * error counter to at least indicate that * this condition happened. */ dtrace_error(&state->dts_dblerrors); continue; } if (vtime) { /* * Before recursing on dtrace_probe(), we * need to explicitly clear out our start * time to prevent it from being accumulated * into t_dtrace_vtime. */ curthread->t_dtrace_start = 0; } /* * Iterate over the actions to figure out which action * we were processing when we experienced the error. * Note that act points _past_ the faulting action; if * act is ecb->dte_action, the fault was in the * predicate, if it's ecb->dte_action->dta_next it's * in action #1, and so on. */ for (err = ecb->dte_action, ndx = 0; err != act; err = err->dta_next, ndx++) continue; dtrace_probe_error(state, ecb->dte_epid, ndx, (mstate.dtms_present & DTRACE_MSTATE_FLTOFFS) ? mstate.dtms_fltoffs : -1, DTRACE_FLAGS2FLT(*flags), cpu_core[cpuid].cpuc_dtrace_illval); continue; } if (!committed) buf->dtb_offset = offs + ecb->dte_size; } if (vtime) curthread->t_dtrace_start = dtrace_gethrtime(); dtrace_interrupt_enable(cookie); } /* * DTrace Probe Hashing Functions * * The functions in this section (and indeed, the functions in remaining * sections) are not _called_ from probe context. (Any exceptions to this are * marked with a "Note:".) Rather, they are called from elsewhere in the * DTrace framework to look-up probes in, add probes to and remove probes from * the DTrace probe hashes. (Each probe is hashed by each element of the * probe tuple -- allowing for fast lookups, regardless of what was * specified.) */ static uint_t dtrace_hash_str(const char *p) { unsigned int g; uint_t hval = 0; while (*p) { hval = (hval << 4) + *p++; if ((g = (hval & 0xf0000000)) != 0) hval ^= g >> 24; hval &= ~g; } return (hval); } static dtrace_hash_t * dtrace_hash_create(uintptr_t stroffs, uintptr_t nextoffs, uintptr_t prevoffs) { dtrace_hash_t *hash = kmem_zalloc(sizeof (dtrace_hash_t), KM_SLEEP); hash->dth_stroffs = stroffs; hash->dth_nextoffs = nextoffs; hash->dth_prevoffs = prevoffs; hash->dth_size = 1; hash->dth_mask = hash->dth_size - 1; hash->dth_tab = kmem_zalloc(hash->dth_size * sizeof (dtrace_hashbucket_t *), KM_SLEEP); return (hash); } static void dtrace_hash_destroy(dtrace_hash_t *hash) { #ifdef DEBUG int i; for (i = 0; i < hash->dth_size; i++) ASSERT(hash->dth_tab[i] == NULL); #endif kmem_free(hash->dth_tab, hash->dth_size * sizeof (dtrace_hashbucket_t *)); kmem_free(hash, sizeof (dtrace_hash_t)); } static void dtrace_hash_resize(dtrace_hash_t *hash) { int size = hash->dth_size, i, ndx; int new_size = hash->dth_size << 1; int new_mask = new_size - 1; dtrace_hashbucket_t **new_tab, *bucket, *next; ASSERT((new_size & new_mask) == 0); new_tab = kmem_zalloc(new_size * sizeof (void *), KM_SLEEP); for (i = 0; i < size; i++) { for (bucket = hash->dth_tab[i]; bucket != NULL; bucket = next) { dtrace_probe_t *probe = bucket->dthb_chain; ASSERT(probe != NULL); ndx = DTRACE_HASHSTR(hash, probe) & new_mask; next = bucket->dthb_next; bucket->dthb_next = new_tab[ndx]; new_tab[ndx] = bucket; } } kmem_free(hash->dth_tab, hash->dth_size * sizeof (void *)); hash->dth_tab = new_tab; hash->dth_size = new_size; hash->dth_mask = new_mask; } static void dtrace_hash_add(dtrace_hash_t *hash, dtrace_probe_t *new) { int hashval = DTRACE_HASHSTR(hash, new); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; dtrace_probe_t **nextp, **prevp; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, new)) goto add; } if ((hash->dth_nbuckets >> 1) > hash->dth_size) { dtrace_hash_resize(hash); dtrace_hash_add(hash, new); return; } bucket = kmem_zalloc(sizeof (dtrace_hashbucket_t), KM_SLEEP); bucket->dthb_next = hash->dth_tab[ndx]; hash->dth_tab[ndx] = bucket; hash->dth_nbuckets++; add: nextp = DTRACE_HASHNEXT(hash, new); ASSERT(*nextp == NULL && *(DTRACE_HASHPREV(hash, new)) == NULL); *nextp = bucket->dthb_chain; if (bucket->dthb_chain != NULL) { prevp = DTRACE_HASHPREV(hash, bucket->dthb_chain); ASSERT(*prevp == NULL); *prevp = new; } bucket->dthb_chain = new; bucket->dthb_len++; } static dtrace_probe_t * dtrace_hash_lookup(dtrace_hash_t *hash, dtrace_probe_t *template) { int hashval = DTRACE_HASHSTR(hash, template); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template)) return (bucket->dthb_chain); } return (NULL); } static int dtrace_hash_collisions(dtrace_hash_t *hash, dtrace_probe_t *template) { int hashval = DTRACE_HASHSTR(hash, template); int ndx = hashval & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template)) return (bucket->dthb_len); } return (0); } static void dtrace_hash_remove(dtrace_hash_t *hash, dtrace_probe_t *probe) { int ndx = DTRACE_HASHSTR(hash, probe) & hash->dth_mask; dtrace_hashbucket_t *bucket = hash->dth_tab[ndx]; dtrace_probe_t **prevp = DTRACE_HASHPREV(hash, probe); dtrace_probe_t **nextp = DTRACE_HASHNEXT(hash, probe); /* * Find the bucket that we're removing this probe from. */ for (; bucket != NULL; bucket = bucket->dthb_next) { if (DTRACE_HASHEQ(hash, bucket->dthb_chain, probe)) break; } ASSERT(bucket != NULL); if (*prevp == NULL) { if (*nextp == NULL) { /* * The removed probe was the only probe on this * bucket; we need to remove the bucket. */ dtrace_hashbucket_t *b = hash->dth_tab[ndx]; ASSERT(bucket->dthb_chain == probe); ASSERT(b != NULL); if (b == bucket) { hash->dth_tab[ndx] = bucket->dthb_next; } else { while (b->dthb_next != bucket) b = b->dthb_next; b->dthb_next = bucket->dthb_next; } ASSERT(hash->dth_nbuckets > 0); hash->dth_nbuckets--; kmem_free(bucket, sizeof (dtrace_hashbucket_t)); return; } bucket->dthb_chain = *nextp; } else { *(DTRACE_HASHNEXT(hash, *prevp)) = *nextp; } if (*nextp != NULL) *(DTRACE_HASHPREV(hash, *nextp)) = *prevp; } /* * DTrace Utility Functions * * These are random utility functions that are _not_ called from probe context. */ static int dtrace_badattr(const dtrace_attribute_t *a) { return (a->dtat_name > DTRACE_STABILITY_MAX || a->dtat_data > DTRACE_STABILITY_MAX || a->dtat_class > DTRACE_CLASS_MAX); } /* * Return a duplicate copy of a string. If the specified string is NULL, * this function returns a zero-length string. */ static char * dtrace_strdup(const char *str) { char *new = kmem_zalloc((str != NULL ? strlen(str) : 0) + 1, KM_SLEEP); if (str != NULL) (void) strcpy(new, str); return (new); } #define DTRACE_ISALPHA(c) \ (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) static int dtrace_badname(const char *s) { char c; if (s == NULL || (c = *s++) == '\0') return (0); if (!DTRACE_ISALPHA(c) && c != '-' && c != '_' && c != '.') return (1); while ((c = *s++) != '\0') { if (!DTRACE_ISALPHA(c) && (c < '0' || c > '9') && c != '-' && c != '_' && c != '.' && c != '`') return (1); } return (0); } static void dtrace_cred2priv(cred_t *cr, uint32_t *privp, uid_t *uidp, zoneid_t *zoneidp) { uint32_t priv; #ifdef illumos if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) { /* * For DTRACE_PRIV_ALL, the uid and zoneid don't matter. */ priv = DTRACE_PRIV_ALL; } else { *uidp = crgetuid(cr); *zoneidp = crgetzoneid(cr); priv = 0; if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE)) priv |= DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER; else if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE)) priv |= DTRACE_PRIV_USER; if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) priv |= DTRACE_PRIV_PROC; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) priv |= DTRACE_PRIV_OWNER; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) priv |= DTRACE_PRIV_ZONEOWNER; } #else priv = DTRACE_PRIV_ALL; #endif *privp = priv; } #ifdef DTRACE_ERRDEBUG static void dtrace_errdebug(const char *str) { int hval = dtrace_hash_str(str) % DTRACE_ERRHASHSZ; int occupied = 0; mutex_enter(&dtrace_errlock); dtrace_errlast = str; dtrace_errthread = curthread; while (occupied++ < DTRACE_ERRHASHSZ) { if (dtrace_errhash[hval].dter_msg == str) { dtrace_errhash[hval].dter_count++; goto out; } if (dtrace_errhash[hval].dter_msg != NULL) { hval = (hval + 1) % DTRACE_ERRHASHSZ; continue; } dtrace_errhash[hval].dter_msg = str; dtrace_errhash[hval].dter_count = 1; goto out; } panic("dtrace: undersized error hash"); out: mutex_exit(&dtrace_errlock); } #endif /* * DTrace Matching Functions * * These functions are used to match groups of probes, given some elements of * a probe tuple, or some globbed expressions for elements of a probe tuple. */ static int dtrace_match_priv(const dtrace_probe_t *prp, uint32_t priv, uid_t uid, zoneid_t zoneid) { if (priv != DTRACE_PRIV_ALL) { uint32_t ppriv = prp->dtpr_provider->dtpv_priv.dtpp_flags; uint32_t match = priv & ppriv; /* * No PRIV_DTRACE_* privileges... */ if ((priv & (DTRACE_PRIV_PROC | DTRACE_PRIV_USER | DTRACE_PRIV_KERNEL)) == 0) return (0); /* * No matching bits, but there were bits to match... */ if (match == 0 && ppriv != 0) return (0); /* * Need to have permissions to the process, but don't... */ if (((ppriv & ~match) & DTRACE_PRIV_OWNER) != 0 && uid != prp->dtpr_provider->dtpv_priv.dtpp_uid) { return (0); } /* * Need to be in the same zone unless we possess the * privilege to examine all zones. */ if (((ppriv & ~match) & DTRACE_PRIV_ZONEOWNER) != 0 && zoneid != prp->dtpr_provider->dtpv_priv.dtpp_zoneid) { return (0); } } return (1); } /* * dtrace_match_probe compares a dtrace_probe_t to a pre-compiled key, which * consists of input pattern strings and an ops-vector to evaluate them. * This function returns >0 for match, 0 for no match, and <0 for error. */ static int dtrace_match_probe(const dtrace_probe_t *prp, const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, zoneid_t zoneid) { dtrace_provider_t *pvp = prp->dtpr_provider; int rv; if (pvp->dtpv_defunct) return (0); if ((rv = pkp->dtpk_pmatch(pvp->dtpv_name, pkp->dtpk_prov, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_mmatch(prp->dtpr_mod, pkp->dtpk_mod, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_fmatch(prp->dtpr_func, pkp->dtpk_func, 0)) <= 0) return (rv); if ((rv = pkp->dtpk_nmatch(prp->dtpr_name, pkp->dtpk_name, 0)) <= 0) return (rv); if (dtrace_match_priv(prp, priv, uid, zoneid) == 0) return (0); return (rv); } /* * dtrace_match_glob() is a safe kernel implementation of the gmatch(3GEN) * interface for matching a glob pattern 'p' to an input string 's'. Unlike * libc's version, the kernel version only applies to 8-bit ASCII strings. * In addition, all of the recursion cases except for '*' matching have been * unwound. For '*', we still implement recursive evaluation, but a depth * counter is maintained and matching is aborted if we recurse too deep. * The function returns 0 if no match, >0 if match, and <0 if recursion error. */ static int dtrace_match_glob(const char *s, const char *p, int depth) { const char *olds; char s1, c; int gs; if (depth > DTRACE_PROBEKEY_MAXDEPTH) return (-1); if (s == NULL) s = ""; /* treat NULL as empty string */ top: olds = s; s1 = *s++; if (p == NULL) return (0); if ((c = *p++) == '\0') return (s1 == '\0'); switch (c) { case '[': { int ok = 0, notflag = 0; char lc = '\0'; if (s1 == '\0') return (0); if (*p == '!') { notflag = 1; p++; } if ((c = *p++) == '\0') return (0); do { if (c == '-' && lc != '\0' && *p != ']') { if ((c = *p++) == '\0') return (0); if (c == '\\' && (c = *p++) == '\0') return (0); if (notflag) { if (s1 < lc || s1 > c) ok++; else return (0); } else if (lc <= s1 && s1 <= c) ok++; } else if (c == '\\' && (c = *p++) == '\0') return (0); lc = c; /* save left-hand 'c' for next iteration */ if (notflag) { if (s1 != c) ok++; else return (0); } else if (s1 == c) ok++; if ((c = *p++) == '\0') return (0); } while (c != ']'); if (ok) goto top; return (0); } case '\\': if ((c = *p++) == '\0') return (0); /*FALLTHRU*/ default: if (c != s1) return (0); /*FALLTHRU*/ case '?': if (s1 != '\0') goto top; return (0); case '*': while (*p == '*') p++; /* consecutive *'s are identical to a single one */ if (*p == '\0') return (1); for (s = olds; *s != '\0'; s++) { if ((gs = dtrace_match_glob(s, p, depth + 1)) != 0) return (gs); } return (0); } } /*ARGSUSED*/ static int dtrace_match_string(const char *s, const char *p, int depth) { return (s != NULL && strcmp(s, p) == 0); } /*ARGSUSED*/ static int dtrace_match_nul(const char *s, const char *p, int depth) { return (1); /* always match the empty pattern */ } /*ARGSUSED*/ static int dtrace_match_nonzero(const char *s, const char *p, int depth) { return (s != NULL && s[0] != '\0'); } static int dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, zoneid_t zoneid, int (*matched)(dtrace_probe_t *, void *), void *arg) { dtrace_probe_t template, *probe; dtrace_hash_t *hash = NULL; int len, best = INT_MAX, nmatched = 0; dtrace_id_t i; ASSERT(MUTEX_HELD(&dtrace_lock)); /* * If the probe ID is specified in the key, just lookup by ID and * invoke the match callback once if a matching probe is found. */ if (pkp->dtpk_id != DTRACE_IDNONE) { if ((probe = dtrace_probe_lookup_id(pkp->dtpk_id)) != NULL && dtrace_match_probe(probe, pkp, priv, uid, zoneid) > 0) { (void) (*matched)(probe, arg); nmatched++; } return (nmatched); } template.dtpr_mod = (char *)pkp->dtpk_mod; template.dtpr_func = (char *)pkp->dtpk_func; template.dtpr_name = (char *)pkp->dtpk_name; /* * We want to find the most distinct of the module name, function * name, and name. So for each one that is not a glob pattern or * empty string, we perform a lookup in the corresponding hash and * use the hash table with the fewest collisions to do our search. */ if (pkp->dtpk_mmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_bymod, &template)) < best) { best = len; hash = dtrace_bymod; } if (pkp->dtpk_fmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_byfunc, &template)) < best) { best = len; hash = dtrace_byfunc; } if (pkp->dtpk_nmatch == &dtrace_match_string && (len = dtrace_hash_collisions(dtrace_byname, &template)) < best) { best = len; hash = dtrace_byname; } /* * If we did not select a hash table, iterate over every probe and * invoke our callback for each one that matches our input probe key. */ if (hash == NULL) { for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL || dtrace_match_probe(probe, pkp, priv, uid, zoneid) <= 0) continue; nmatched++; if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) break; } return (nmatched); } /* * If we selected a hash table, iterate over each probe of the same key * name and invoke the callback for every probe that matches the other * attributes of our input probe key. */ for (probe = dtrace_hash_lookup(hash, &template); probe != NULL; probe = *(DTRACE_HASHNEXT(hash, probe))) { if (dtrace_match_probe(probe, pkp, priv, uid, zoneid) <= 0) continue; nmatched++; if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) break; } return (nmatched); } /* * Return the function pointer dtrace_probecmp() should use to compare the * specified pattern with a string. For NULL or empty patterns, we select * dtrace_match_nul(). For glob pattern strings, we use dtrace_match_glob(). * For non-empty non-glob strings, we use dtrace_match_string(). */ static dtrace_probekey_f * dtrace_probekey_func(const char *p) { char c; if (p == NULL || *p == '\0') return (&dtrace_match_nul); while ((c = *p++) != '\0') { if (c == '[' || c == '?' || c == '*' || c == '\\') return (&dtrace_match_glob); } return (&dtrace_match_string); } /* * Build a probe comparison key for use with dtrace_match_probe() from the * given probe description. By convention, a null key only matches anchored * probes: if each field is the empty string, reset dtpk_fmatch to * dtrace_match_nonzero(). */ static void dtrace_probekey(dtrace_probedesc_t *pdp, dtrace_probekey_t *pkp) { pkp->dtpk_prov = pdp->dtpd_provider; pkp->dtpk_pmatch = dtrace_probekey_func(pdp->dtpd_provider); pkp->dtpk_mod = pdp->dtpd_mod; pkp->dtpk_mmatch = dtrace_probekey_func(pdp->dtpd_mod); pkp->dtpk_func = pdp->dtpd_func; pkp->dtpk_fmatch = dtrace_probekey_func(pdp->dtpd_func); pkp->dtpk_name = pdp->dtpd_name; pkp->dtpk_nmatch = dtrace_probekey_func(pdp->dtpd_name); pkp->dtpk_id = pdp->dtpd_id; if (pkp->dtpk_id == DTRACE_IDNONE && pkp->dtpk_pmatch == &dtrace_match_nul && pkp->dtpk_mmatch == &dtrace_match_nul && pkp->dtpk_fmatch == &dtrace_match_nul && pkp->dtpk_nmatch == &dtrace_match_nul) pkp->dtpk_fmatch = &dtrace_match_nonzero; } /* * DTrace Provider-to-Framework API Functions * * These functions implement much of the Provider-to-Framework API, as * described in . The parts of the API not in this section are * the functions in the API for probe management (found below), and * dtrace_probe() itself (found above). */ /* * Register the calling provider with the DTrace framework. This should * generally be called by DTrace providers in their attach(9E) entry point. */ int dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv, cred_t *cr, const dtrace_pops_t *pops, void *arg, dtrace_provider_id_t *idp) { dtrace_provider_t *provider; if (name == NULL || pap == NULL || pops == NULL || idp == NULL) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "arguments", name ? name : ""); return (EINVAL); } if (name[0] == '\0' || dtrace_badname(name)) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider name", name); return (EINVAL); } if ((pops->dtps_provide == NULL && pops->dtps_provide_module == NULL) || pops->dtps_enable == NULL || pops->dtps_disable == NULL || pops->dtps_destroy == NULL || ((pops->dtps_resume == NULL) != (pops->dtps_suspend == NULL))) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider ops", name); return (EINVAL); } if (dtrace_badattr(&pap->dtpa_provider) || dtrace_badattr(&pap->dtpa_mod) || dtrace_badattr(&pap->dtpa_func) || dtrace_badattr(&pap->dtpa_name) || dtrace_badattr(&pap->dtpa_args)) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "provider attributes", name); return (EINVAL); } if (priv & ~DTRACE_PRIV_ALL) { cmn_err(CE_WARN, "failed to register provider '%s': invalid " "privilege attributes", name); return (EINVAL); } if ((priv & DTRACE_PRIV_KERNEL) && (priv & (DTRACE_PRIV_USER | DTRACE_PRIV_OWNER)) && pops->dtps_usermode == NULL) { cmn_err(CE_WARN, "failed to register provider '%s': need " "dtps_usermode() op for given privilege attributes", name); return (EINVAL); } provider = kmem_zalloc(sizeof (dtrace_provider_t), KM_SLEEP); provider->dtpv_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); (void) strcpy(provider->dtpv_name, name); provider->dtpv_attr = *pap; provider->dtpv_priv.dtpp_flags = priv; if (cr != NULL) { provider->dtpv_priv.dtpp_uid = crgetuid(cr); provider->dtpv_priv.dtpp_zoneid = crgetzoneid(cr); } provider->dtpv_pops = *pops; if (pops->dtps_provide == NULL) { ASSERT(pops->dtps_provide_module != NULL); provider->dtpv_pops.dtps_provide = (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop; } if (pops->dtps_provide_module == NULL) { ASSERT(pops->dtps_provide != NULL); provider->dtpv_pops.dtps_provide_module = (void (*)(void *, modctl_t *))dtrace_nullop; } if (pops->dtps_suspend == NULL) { ASSERT(pops->dtps_resume == NULL); provider->dtpv_pops.dtps_suspend = (void (*)(void *, dtrace_id_t, void *))dtrace_nullop; provider->dtpv_pops.dtps_resume = (void (*)(void *, dtrace_id_t, void *))dtrace_nullop; } provider->dtpv_arg = arg; *idp = (dtrace_provider_id_t)provider; if (pops == &dtrace_provider_ops) { ASSERT(MUTEX_HELD(&dtrace_provider_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dtrace_anon.dta_enabling == NULL); /* * We make sure that the DTrace provider is at the head of * the provider chain. */ provider->dtpv_next = dtrace_provider; dtrace_provider = provider; return (0); } mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); /* * If there is at least one provider registered, we'll add this * provider after the first provider. */ if (dtrace_provider != NULL) { provider->dtpv_next = dtrace_provider->dtpv_next; dtrace_provider->dtpv_next = provider; } else { dtrace_provider = provider; } if (dtrace_retained != NULL) { dtrace_enabling_provide(provider); /* * Now we need to call dtrace_enabling_matchall() -- which * will acquire cpu_lock and dtrace_lock. We therefore need * to drop all of our locks before calling into it... */ mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); dtrace_enabling_matchall(); return (0); } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (0); } /* * Unregister the specified provider from the DTrace framework. This should * generally be called by DTrace providers in their detach(9E) entry point. */ int dtrace_unregister(dtrace_provider_id_t id) { dtrace_provider_t *old = (dtrace_provider_t *)id; dtrace_provider_t *prev = NULL; int i, self = 0, noreap = 0; dtrace_probe_t *probe, *first = NULL; if (old->dtpv_pops.dtps_enable == (void (*)(void *, dtrace_id_t, void *))dtrace_nullop) { /* * If DTrace itself is the provider, we're called with locks * already held. */ ASSERT(old == dtrace_provider); #ifdef illumos ASSERT(dtrace_devi != NULL); #endif ASSERT(MUTEX_HELD(&dtrace_provider_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); self = 1; if (dtrace_provider->dtpv_next != NULL) { /* * There's another provider here; return failure. */ return (EBUSY); } } else { mutex_enter(&dtrace_provider_lock); #ifdef illumos mutex_enter(&mod_lock); #endif mutex_enter(&dtrace_lock); } /* * If anyone has /dev/dtrace open, or if there are anonymous enabled * probes, we refuse to let providers slither away, unless this * provider has already been explicitly invalidated. */ if (!old->dtpv_defunct && (dtrace_opens || (dtrace_anon.dta_state != NULL && dtrace_anon.dta_state->dts_necbs > 0))) { if (!self) { mutex_exit(&dtrace_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } return (EBUSY); } /* * Attempt to destroy the probes associated with this provider. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != old) continue; if (probe->dtpr_ecb == NULL) continue; /* * If we are trying to unregister a defunct provider, and the * provider was made defunct within the interval dictated by * dtrace_unregister_defunct_reap, we'll (asynchronously) * attempt to reap our enablings. To denote that the provider * should reattempt to unregister itself at some point in the * future, we will return a differentiable error code (EAGAIN * instead of EBUSY) in this case. */ if (dtrace_gethrtime() - old->dtpv_defunct > dtrace_unregister_defunct_reap) noreap = 1; if (!self) { mutex_exit(&dtrace_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } if (noreap) return (EBUSY); (void) taskq_dispatch(dtrace_taskq, (task_func_t *)dtrace_enabling_reap, NULL, TQ_SLEEP); return (EAGAIN); } /* * All of the probes for this provider are disabled; we can safely * remove all of them from their hash chains and from the probe array. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != old) continue; dtrace_probes[i] = NULL; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); if (first == NULL) { first = probe; probe->dtpr_nextmod = NULL; } else { probe->dtpr_nextmod = first; first = probe; } } /* * The provider's probes have been removed from the hash chains and * from the probe array. Now issue a dtrace_sync() to be sure that * everyone has cleared out from any probe array processing. */ dtrace_sync(); for (probe = first; probe != NULL; probe = first) { first = probe->dtpr_nextmod; old->dtpv_pops.dtps_destroy(old->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); #ifdef illumos vmem_free(dtrace_arena, (void *)(uintptr_t)(probe->dtpr_id), 1); #else free_unr(dtrace_arena, probe->dtpr_id); #endif kmem_free(probe, sizeof (dtrace_probe_t)); } if ((prev = dtrace_provider) == old) { #ifdef illumos ASSERT(self || dtrace_devi == NULL); ASSERT(old->dtpv_next == NULL || dtrace_devi == NULL); #endif dtrace_provider = old->dtpv_next; } else { while (prev != NULL && prev->dtpv_next != old) prev = prev->dtpv_next; if (prev == NULL) { panic("attempt to unregister non-existent " "dtrace provider %p\n", (void *)id); } prev->dtpv_next = old->dtpv_next; } if (!self) { mutex_exit(&dtrace_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } kmem_free(old->dtpv_name, strlen(old->dtpv_name) + 1); kmem_free(old, sizeof (dtrace_provider_t)); return (0); } /* * Invalidate the specified provider. All subsequent probe lookups for the * specified provider will fail, but its probes will not be removed. */ void dtrace_invalidate(dtrace_provider_id_t id) { dtrace_provider_t *pvp = (dtrace_provider_t *)id; ASSERT(pvp->dtpv_pops.dtps_enable != (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); pvp->dtpv_defunct = dtrace_gethrtime(); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); } /* * Indicate whether or not DTrace has attached. */ int dtrace_attached(void) { /* * dtrace_provider will be non-NULL iff the DTrace driver has * attached. (It's non-NULL because DTrace is always itself a * provider.) */ return (dtrace_provider != NULL); } /* * Remove all the unenabled probes for the given provider. This function is * not unlike dtrace_unregister(), except that it doesn't remove the provider * -- just as many of its associated probes as it can. */ int dtrace_condense(dtrace_provider_id_t id) { dtrace_provider_t *prov = (dtrace_provider_t *)id; int i; dtrace_probe_t *probe; /* * Make sure this isn't the dtrace provider itself. */ ASSERT(prov->dtpv_pops.dtps_enable != (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); /* * Attempt to destroy the probes associated with this provider. */ for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_provider != prov) continue; if (probe->dtpr_ecb != NULL) continue; dtrace_probes[i] = NULL; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, i + 1, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); kmem_free(probe, sizeof (dtrace_probe_t)); #ifdef illumos vmem_free(dtrace_arena, (void *)((uintptr_t)i + 1), 1); #else free_unr(dtrace_arena, i + 1); #endif } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (0); } /* * DTrace Probe Management Functions * * The functions in this section perform the DTrace probe management, * including functions to create probes, look-up probes, and call into the * providers to request that probes be provided. Some of these functions are * in the Provider-to-Framework API; these functions can be identified by the * fact that they are not declared "static". */ /* * Create a probe with the specified module name, function name, and name. */ dtrace_id_t dtrace_probe_create(dtrace_provider_id_t prov, const char *mod, const char *func, const char *name, int aframes, void *arg) { dtrace_probe_t *probe, **probes; dtrace_provider_t *provider = (dtrace_provider_t *)prov; dtrace_id_t id; if (provider == dtrace_provider) { ASSERT(MUTEX_HELD(&dtrace_lock)); } else { mutex_enter(&dtrace_lock); } #ifdef illumos id = (dtrace_id_t)(uintptr_t)vmem_alloc(dtrace_arena, 1, VM_BESTFIT | VM_SLEEP); #else id = alloc_unr(dtrace_arena); #endif probe = kmem_zalloc(sizeof (dtrace_probe_t), KM_SLEEP); probe->dtpr_id = id; probe->dtpr_gen = dtrace_probegen++; probe->dtpr_mod = dtrace_strdup(mod); probe->dtpr_func = dtrace_strdup(func); probe->dtpr_name = dtrace_strdup(name); probe->dtpr_arg = arg; probe->dtpr_aframes = aframes; probe->dtpr_provider = provider; dtrace_hash_add(dtrace_bymod, probe); dtrace_hash_add(dtrace_byfunc, probe); dtrace_hash_add(dtrace_byname, probe); if (id - 1 >= dtrace_nprobes) { size_t osize = dtrace_nprobes * sizeof (dtrace_probe_t *); size_t nsize = osize << 1; if (nsize == 0) { ASSERT(osize == 0); ASSERT(dtrace_probes == NULL); nsize = sizeof (dtrace_probe_t *); } probes = kmem_zalloc(nsize, KM_SLEEP); if (dtrace_probes == NULL) { ASSERT(osize == 0); dtrace_probes = probes; dtrace_nprobes = 1; } else { dtrace_probe_t **oprobes = dtrace_probes; bcopy(oprobes, probes, osize); dtrace_membar_producer(); dtrace_probes = probes; dtrace_sync(); /* * All CPUs are now seeing the new probes array; we can * safely free the old array. */ kmem_free(oprobes, osize); dtrace_nprobes <<= 1; } ASSERT(id - 1 < dtrace_nprobes); } ASSERT(dtrace_probes[id - 1] == NULL); dtrace_probes[id - 1] = probe; if (provider != dtrace_provider) mutex_exit(&dtrace_lock); return (id); } static dtrace_probe_t * dtrace_probe_lookup_id(dtrace_id_t id) { ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > dtrace_nprobes) return (NULL); return (dtrace_probes[id - 1]); } static int dtrace_probe_lookup_match(dtrace_probe_t *probe, void *arg) { *((dtrace_id_t *)arg) = probe->dtpr_id; return (DTRACE_MATCH_DONE); } /* * Look up a probe based on provider and one or more of module name, function * name and probe name. */ dtrace_id_t dtrace_probe_lookup(dtrace_provider_id_t prid, char *mod, char *func, char *name) { dtrace_probekey_t pkey; dtrace_id_t id; int match; pkey.dtpk_prov = ((dtrace_provider_t *)prid)->dtpv_name; pkey.dtpk_pmatch = &dtrace_match_string; pkey.dtpk_mod = mod; pkey.dtpk_mmatch = mod ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_func = func; pkey.dtpk_fmatch = func ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_name = name; pkey.dtpk_nmatch = name ? &dtrace_match_string : &dtrace_match_nul; pkey.dtpk_id = DTRACE_IDNONE; mutex_enter(&dtrace_lock); match = dtrace_match(&pkey, DTRACE_PRIV_ALL, 0, 0, dtrace_probe_lookup_match, &id); mutex_exit(&dtrace_lock); ASSERT(match == 1 || match == 0); return (match ? id : 0); } /* * Returns the probe argument associated with the specified probe. */ void * dtrace_probe_arg(dtrace_provider_id_t id, dtrace_id_t pid) { dtrace_probe_t *probe; void *rval = NULL; mutex_enter(&dtrace_lock); if ((probe = dtrace_probe_lookup_id(pid)) != NULL && probe->dtpr_provider == (dtrace_provider_t *)id) rval = probe->dtpr_arg; mutex_exit(&dtrace_lock); return (rval); } /* * Copy a probe into a probe description. */ static void dtrace_probe_description(const dtrace_probe_t *prp, dtrace_probedesc_t *pdp) { bzero(pdp, sizeof (dtrace_probedesc_t)); pdp->dtpd_id = prp->dtpr_id; (void) strncpy(pdp->dtpd_provider, prp->dtpr_provider->dtpv_name, DTRACE_PROVNAMELEN - 1); (void) strncpy(pdp->dtpd_mod, prp->dtpr_mod, DTRACE_MODNAMELEN - 1); (void) strncpy(pdp->dtpd_func, prp->dtpr_func, DTRACE_FUNCNAMELEN - 1); (void) strncpy(pdp->dtpd_name, prp->dtpr_name, DTRACE_NAMELEN - 1); } /* * Called to indicate that a probe -- or probes -- should be provided by a * specfied provider. If the specified description is NULL, the provider will * be told to provide all of its probes. (This is done whenever a new * consumer comes along, or whenever a retained enabling is to be matched.) If * the specified description is non-NULL, the provider is given the * opportunity to dynamically provide the specified probe, allowing providers * to support the creation of probes on-the-fly. (So-called _autocreated_ * probes.) If the provider is NULL, the operations will be applied to all * providers; if the provider is non-NULL the operations will only be applied * to the specified provider. The dtrace_provider_lock must be held, and the * dtrace_lock must _not_ be held -- the provider's dtps_provide() operation * will need to grab the dtrace_lock when it reenters the framework through * dtrace_probe_lookup(), dtrace_probe_create(), etc. */ static void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) { #ifdef illumos modctl_t *ctl; #endif int all = 0; ASSERT(MUTEX_HELD(&dtrace_provider_lock)); if (prv == NULL) { all = 1; prv = dtrace_provider; } do { /* * First, call the blanket provide operation. */ prv->dtpv_pops.dtps_provide(prv->dtpv_arg, desc); #ifdef illumos /* * Now call the per-module provide operation. We will grab * mod_lock to prevent the list from being modified. Note * that this also prevents the mod_busy bits from changing. * (mod_busy can only be changed with mod_lock held.) */ mutex_enter(&mod_lock); ctl = &modules; do { if (ctl->mod_busy || ctl->mod_mp == NULL) continue; prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl); } while ((ctl = ctl->mod_next) != &modules); mutex_exit(&mod_lock); #endif } while (all && (prv = prv->dtpv_next) != NULL); } #ifdef illumos /* * Iterate over each probe, and call the Framework-to-Provider API function * denoted by offs. */ static void dtrace_probe_foreach(uintptr_t offs) { dtrace_provider_t *prov; void (*func)(void *, dtrace_id_t, void *); dtrace_probe_t *probe; dtrace_icookie_t cookie; int i; /* * We disable interrupts to walk through the probe array. This is * safe -- the dtrace_sync() in dtrace_unregister() assures that we * won't see stale data. */ cookie = dtrace_interrupt_disable(); for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_ecb == NULL) { /* * This probe isn't enabled -- don't call the function. */ continue; } prov = probe->dtpr_provider; func = *((void(**)(void *, dtrace_id_t, void *)) ((uintptr_t)&prov->dtpv_pops + offs)); func(prov->dtpv_arg, i + 1, probe->dtpr_arg); } dtrace_interrupt_enable(cookie); } #endif static int dtrace_probe_enable(dtrace_probedesc_t *desc, dtrace_enabling_t *enab) { dtrace_probekey_t pkey; uint32_t priv; uid_t uid; zoneid_t zoneid; ASSERT(MUTEX_HELD(&dtrace_lock)); dtrace_ecb_create_cache = NULL; if (desc == NULL) { /* * If we're passed a NULL description, we're being asked to * create an ECB with a NULL probe. */ (void) dtrace_ecb_create_enable(NULL, enab); return (0); } dtrace_probekey(desc, &pkey); dtrace_cred2priv(enab->dten_vstate->dtvs_state->dts_cred.dcr_cred, &priv, &uid, &zoneid); return (dtrace_match(&pkey, priv, uid, zoneid, dtrace_ecb_create_enable, enab)); } /* * DTrace Helper Provider Functions */ static void dtrace_dofattr2attr(dtrace_attribute_t *attr, const dof_attr_t dofattr) { attr->dtat_name = DOF_ATTR_NAME(dofattr); attr->dtat_data = DOF_ATTR_DATA(dofattr); attr->dtat_class = DOF_ATTR_CLASS(dofattr); } static void dtrace_dofprov2hprov(dtrace_helper_provdesc_t *hprov, const dof_provider_t *dofprov, char *strtab) { hprov->dthpv_provname = strtab + dofprov->dofpv_name; dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_provider, dofprov->dofpv_provattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_mod, dofprov->dofpv_modattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_func, dofprov->dofpv_funcattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_name, dofprov->dofpv_nameattr); dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_args, dofprov->dofpv_argsattr); } static void dtrace_helper_provide_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec; dof_provider_t *provider; dof_probe_t *probe; uint32_t *off, *enoff; uint8_t *arg; char *strtab; uint_t i, nprobes; dtrace_helper_provdesc_t dhpv; dtrace_helper_probedesc_t dhpb; dtrace_meta_t *meta = dtrace_meta_pid; dtrace_mops_t *mops = &meta->dtm_mops; void *parg; provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_strtab * dof->dofh_secsize); prb_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_probes * dof->dofh_secsize); arg_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_prargs * dof->dofh_secsize); off_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_proffs * dof->dofh_secsize); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); off = (uint32_t *)(uintptr_t)(daddr + off_sec->dofs_offset); arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset); enoff = NULL; /* * See dtrace_helper_provider_validate(). */ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && provider->dofpv_prenoffs != DOF_SECT_NONE) { enoff_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_prenoffs * dof->dofh_secsize); enoff = (uint32_t *)(uintptr_t)(daddr + enoff_sec->dofs_offset); } nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize; /* * Create the provider. */ dtrace_dofprov2hprov(&dhpv, provider, strtab); if ((parg = mops->dtms_provide_pid(meta->dtm_arg, &dhpv, pid)) == NULL) return; meta->dtm_count++; /* * Create the probes. */ for (i = 0; i < nprobes; i++) { probe = (dof_probe_t *)(uintptr_t)(daddr + prb_sec->dofs_offset + i * prb_sec->dofs_entsize); dhpb.dthpb_mod = dhp->dofhp_mod; dhpb.dthpb_func = strtab + probe->dofpr_func; dhpb.dthpb_name = strtab + probe->dofpr_name; dhpb.dthpb_base = probe->dofpr_addr; dhpb.dthpb_offs = off + probe->dofpr_offidx; dhpb.dthpb_noffs = probe->dofpr_noffs; if (enoff != NULL) { dhpb.dthpb_enoffs = enoff + probe->dofpr_enoffidx; dhpb.dthpb_nenoffs = probe->dofpr_nenoffs; } else { dhpb.dthpb_enoffs = NULL; dhpb.dthpb_nenoffs = 0; } dhpb.dthpb_args = arg + probe->dofpr_argidx; dhpb.dthpb_nargc = probe->dofpr_nargc; dhpb.dthpb_xargc = probe->dofpr_xargc; dhpb.dthpb_ntypes = strtab + probe->dofpr_nargv; dhpb.dthpb_xtypes = strtab + probe->dofpr_xargv; mops->dtms_create_probe(meta->dtm_arg, parg, &dhpb); } } static void dtrace_helper_provide(dof_helper_t *dhp, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; int i; ASSERT(MUTEX_HELD(&dtrace_meta_lock)); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; dtrace_helper_provide_one(dhp, sec, pid); } /* * We may have just created probes, so we must now rematch against * any retained enablings. Note that this call will acquire both * cpu_lock and dtrace_lock; the fact that we are holding * dtrace_meta_lock now is what defines the ordering with respect to * these three locks. */ dtrace_enabling_matchall(); } static void dtrace_helper_provider_remove_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; dof_sec_t *str_sec; dof_provider_t *provider; char *strtab; dtrace_helper_provdesc_t dhpv; dtrace_meta_t *meta = dtrace_meta_pid; dtrace_mops_t *mops = &meta->dtm_mops; provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + provider->dofpv_strtab * dof->dofh_secsize); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); /* * Create the provider. */ dtrace_dofprov2hprov(&dhpv, provider, strtab); mops->dtms_remove_pid(meta->dtm_arg, &dhpv, pid); meta->dtm_count--; } static void dtrace_helper_provider_remove(dof_helper_t *dhp, pid_t pid) { uintptr_t daddr = (uintptr_t)dhp->dofhp_dof; dof_hdr_t *dof = (dof_hdr_t *)daddr; int i; ASSERT(MUTEX_HELD(&dtrace_meta_lock)); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; dtrace_helper_provider_remove_one(dhp, sec, pid); } } /* * DTrace Meta Provider-to-Framework API Functions * * These functions implement the Meta Provider-to-Framework API, as described * in . */ int dtrace_meta_register(const char *name, const dtrace_mops_t *mops, void *arg, dtrace_meta_provider_id_t *idp) { dtrace_meta_t *meta; dtrace_helpers_t *help, *next; int i; *idp = DTRACE_METAPROVNONE; /* * We strictly don't need the name, but we hold onto it for * debuggability. All hail error queues! */ if (name == NULL) { cmn_err(CE_WARN, "failed to register meta-provider: " "invalid name"); return (EINVAL); } if (mops == NULL || mops->dtms_create_probe == NULL || mops->dtms_provide_pid == NULL || mops->dtms_remove_pid == NULL) { cmn_err(CE_WARN, "failed to register meta-register %s: " "invalid ops", name); return (EINVAL); } meta = kmem_zalloc(sizeof (dtrace_meta_t), KM_SLEEP); meta->dtm_mops = *mops; meta->dtm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); (void) strcpy(meta->dtm_name, name); meta->dtm_arg = arg; mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (dtrace_meta_pid != NULL) { mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); cmn_err(CE_WARN, "failed to register meta-register %s: " "user-land meta-provider exists", name); kmem_free(meta->dtm_name, strlen(meta->dtm_name) + 1); kmem_free(meta, sizeof (dtrace_meta_t)); return (EINVAL); } dtrace_meta_pid = meta; *idp = (dtrace_meta_provider_id_t)meta; /* * If there are providers and probes ready to go, pass them * off to the new meta provider now. */ help = dtrace_deferred_pid; dtrace_deferred_pid = NULL; mutex_exit(&dtrace_lock); while (help != NULL) { for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov, help->dthps_pid); } next = help->dthps_next; help->dthps_next = NULL; help->dthps_prev = NULL; help->dthps_deferred = 0; help = next; } mutex_exit(&dtrace_meta_lock); return (0); } int dtrace_meta_unregister(dtrace_meta_provider_id_t id) { dtrace_meta_t **pp, *old = (dtrace_meta_t *)id; mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (old == dtrace_meta_pid) { pp = &dtrace_meta_pid; } else { panic("attempt to unregister non-existent " "dtrace meta-provider %p\n", (void *)old); } if (old->dtm_count != 0) { mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); return (EBUSY); } *pp = NULL; mutex_exit(&dtrace_lock); mutex_exit(&dtrace_meta_lock); kmem_free(old->dtm_name, strlen(old->dtm_name) + 1); kmem_free(old, sizeof (dtrace_meta_t)); return (0); } /* * DTrace DIF Object Functions */ static int dtrace_difo_err(uint_t pc, const char *format, ...) { if (dtrace_err_verbose) { va_list alist; (void) uprintf("dtrace DIF object error: [%u]: ", pc); va_start(alist, format); (void) vuprintf(format, alist); va_end(alist); } #ifdef DTRACE_ERRDEBUG dtrace_errdebug(format); #endif return (1); } /* * Validate a DTrace DIF object by checking the IR instructions. The following * rules are currently enforced by dtrace_difo_validate(): * * 1. Each instruction must have a valid opcode * 2. Each register, string, variable, or subroutine reference must be valid * 3. No instruction can modify register %r0 (must be zero) * 4. All instruction reserved bits must be set to zero * 5. The last instruction must be a "ret" instruction * 6. All branch targets must reference a valid instruction _after_ the branch */ static int dtrace_difo_validate(dtrace_difo_t *dp, dtrace_vstate_t *vstate, uint_t nregs, cred_t *cr) { int err = 0, i; int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err; int kcheckload; uint_t pc; kcheckload = cr == NULL || (vstate->dtvs_state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) == 0; dp->dtdo_destructive = 0; for (pc = 0; pc < dp->dtdo_len && err == 0; pc++) { dif_instr_t instr = dp->dtdo_buf[pc]; uint_t r1 = DIF_INSTR_R1(instr); uint_t r2 = DIF_INSTR_R2(instr); uint_t rd = DIF_INSTR_RD(instr); uint_t rs = DIF_INSTR_RS(instr); uint_t label = DIF_INSTR_LABEL(instr); uint_t v = DIF_INSTR_VAR(instr); uint_t subr = DIF_INSTR_SUBR(instr); uint_t type = DIF_INSTR_TYPE(instr); uint_t op = DIF_INSTR_OP(instr); switch (op) { case DIF_OP_OR: case DIF_OP_XOR: case DIF_OP_AND: case DIF_OP_SLL: case DIF_OP_SRL: case DIF_OP_SRA: case DIF_OP_SUB: case DIF_OP_ADD: case DIF_OP_MUL: case DIF_OP_SDIV: case DIF_OP_UDIV: case DIF_OP_SREM: case DIF_OP_UREM: case DIF_OP_COPYS: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_NOT: case DIF_OP_MOV: case DIF_OP_ALLOCS: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDSB: case DIF_OP_LDSH: case DIF_OP_LDSW: case DIF_OP_LDUB: case DIF_OP_LDUH: case DIF_OP_LDUW: case DIF_OP_LDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); if (kcheckload) dp->dtdo_buf[pc] = DIF_INSTR_LOAD(op + DIF_OP_RLDSB - DIF_OP_LDSB, r1, rd); break; case DIF_OP_RLDSB: case DIF_OP_RLDSH: case DIF_OP_RLDSW: case DIF_OP_RLDUB: case DIF_OP_RLDUH: case DIF_OP_RLDUW: case DIF_OP_RLDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_ULDSB: case DIF_OP_ULDSH: case DIF_OP_ULDSW: case DIF_OP_ULDUB: case DIF_OP_ULDUH: case DIF_OP_ULDUW: case DIF_OP_ULDX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_STB: case DIF_OP_STH: case DIF_OP_STW: case DIF_OP_STX: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to 0 address\n"); break; case DIF_OP_CMP: case DIF_OP_SCMP: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_TST: if (r1 >= nregs) err += efunc(pc, "invalid register %u\n", r1); if (r2 != 0 || rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_BA: case DIF_OP_BE: case DIF_OP_BNE: case DIF_OP_BG: case DIF_OP_BGU: case DIF_OP_BGE: case DIF_OP_BGEU: case DIF_OP_BL: case DIF_OP_BLU: case DIF_OP_BLE: case DIF_OP_BLEU: if (label >= dp->dtdo_len) { err += efunc(pc, "invalid branch target %u\n", label); } if (label <= pc) { err += efunc(pc, "backward branch to %u\n", label); } break; case DIF_OP_RET: if (r1 != 0 || r2 != 0) err += efunc(pc, "non-zero reserved bits\n"); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); break; case DIF_OP_NOP: case DIF_OP_POPTS: case DIF_OP_FLUSHTS: if (r1 != 0 || r2 != 0 || rd != 0) err += efunc(pc, "non-zero reserved bits\n"); break; case DIF_OP_SETX: if (DIF_INSTR_INTEGER(instr) >= dp->dtdo_intlen) { err += efunc(pc, "invalid integer ref %u\n", DIF_INSTR_INTEGER(instr)); } if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_SETS: if (DIF_INSTR_STRING(instr) >= dp->dtdo_strlen) { err += efunc(pc, "invalid string ref %u\n", DIF_INSTR_STRING(instr)); } if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDGA: case DIF_OP_LDTA: if (r1 > DIF_VAR_ARRAY_MAX) err += efunc(pc, "invalid array %u\n", r1); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_LDGS: case DIF_OP_LDTS: case DIF_OP_LDLS: case DIF_OP_LDGAA: case DIF_OP_LDTAA: if (v < DIF_VAR_OTHER_MIN || v > DIF_VAR_OTHER_MAX) err += efunc(pc, "invalid variable %u\n", v); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); break; case DIF_OP_STGS: case DIF_OP_STTS: case DIF_OP_STLS: case DIF_OP_STGAA: case DIF_OP_STTAA: if (v < DIF_VAR_OTHER_UBASE || v > DIF_VAR_OTHER_MAX) err += efunc(pc, "invalid variable %u\n", v); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rd); break; case DIF_OP_CALL: if (subr > DIF_SUBR_MAX) err += efunc(pc, "invalid subr %u\n", subr); if (rd >= nregs) err += efunc(pc, "invalid register %u\n", rd); if (rd == 0) err += efunc(pc, "cannot write to %r0\n"); if (subr == DIF_SUBR_COPYOUT || subr == DIF_SUBR_COPYOUTSTR) { dp->dtdo_destructive = 1; } if (subr == DIF_SUBR_GETF) { /* * If we have a getf() we need to record that * in our state. Note that our state can be * NULL if this is a helper -- but in that * case, the call to getf() is itself illegal, * and will be caught (slightly later) when * the helper is validated. */ if (vstate->dtvs_state != NULL) vstate->dtvs_state->dts_getf++; } break; case DIF_OP_PUSHTR: if (type != DIF_TYPE_STRING && type != DIF_TYPE_CTF) err += efunc(pc, "invalid ref type %u\n", type); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rs); break; case DIF_OP_PUSHTV: if (type != DIF_TYPE_CTF) err += efunc(pc, "invalid val type %u\n", type); if (r2 >= nregs) err += efunc(pc, "invalid register %u\n", r2); if (rs >= nregs) err += efunc(pc, "invalid register %u\n", rs); break; default: err += efunc(pc, "invalid opcode %u\n", DIF_INSTR_OP(instr)); } } if (dp->dtdo_len != 0 && DIF_INSTR_OP(dp->dtdo_buf[dp->dtdo_len - 1]) != DIF_OP_RET) { err += efunc(dp->dtdo_len - 1, "expected 'ret' as last DIF instruction\n"); } if (!(dp->dtdo_rtype.dtdt_flags & (DIF_TF_BYREF | DIF_TF_BYUREF))) { /* * If we're not returning by reference, the size must be either * 0 or the size of one of the base types. */ switch (dp->dtdo_rtype.dtdt_size) { case 0: case sizeof (uint8_t): case sizeof (uint16_t): case sizeof (uint32_t): case sizeof (uint64_t): break; default: err += efunc(dp->dtdo_len - 1, "bad return size\n"); } } for (i = 0; i < dp->dtdo_varlen && err == 0; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i], *existing = NULL; dtrace_diftype_t *vt, *et; uint_t id, ndx; if (v->dtdv_scope != DIFV_SCOPE_GLOBAL && v->dtdv_scope != DIFV_SCOPE_THREAD && v->dtdv_scope != DIFV_SCOPE_LOCAL) { err += efunc(i, "unrecognized variable scope %d\n", v->dtdv_scope); break; } if (v->dtdv_kind != DIFV_KIND_ARRAY && v->dtdv_kind != DIFV_KIND_SCALAR) { err += efunc(i, "unrecognized variable type %d\n", v->dtdv_kind); break; } if ((id = v->dtdv_id) > DIF_VARIABLE_MAX) { err += efunc(i, "%d exceeds variable id limit\n", id); break; } if (id < DIF_VAR_OTHER_UBASE) continue; /* * For user-defined variables, we need to check that this * definition is identical to any previous definition that we * encountered. */ ndx = id - DIF_VAR_OTHER_UBASE; switch (v->dtdv_scope) { case DIFV_SCOPE_GLOBAL: if (ndx < vstate->dtvs_nglobals) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_globals[ndx]) != NULL) existing = &svar->dtsv_var; } break; case DIFV_SCOPE_THREAD: if (ndx < vstate->dtvs_ntlocals) existing = &vstate->dtvs_tlocals[ndx]; break; case DIFV_SCOPE_LOCAL: if (ndx < vstate->dtvs_nlocals) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_locals[ndx]) != NULL) existing = &svar->dtsv_var; } break; } vt = &v->dtdv_type; if (vt->dtdt_flags & DIF_TF_BYREF) { if (vt->dtdt_size == 0) { err += efunc(i, "zero-sized variable\n"); break; } if (v->dtdv_scope == DIFV_SCOPE_GLOBAL && vt->dtdt_size > dtrace_global_maxsize) { err += efunc(i, "oversized by-ref global\n"); break; } } if (existing == NULL || existing->dtdv_id == 0) continue; ASSERT(existing->dtdv_id == v->dtdv_id); ASSERT(existing->dtdv_scope == v->dtdv_scope); if (existing->dtdv_kind != v->dtdv_kind) err += efunc(i, "%d changed variable kind\n", id); et = &existing->dtdv_type; if (vt->dtdt_flags != et->dtdt_flags) { err += efunc(i, "%d changed variable type flags\n", id); break; } if (vt->dtdt_size != 0 && vt->dtdt_size != et->dtdt_size) { err += efunc(i, "%d changed variable type size\n", id); break; } } return (err); } /* * Validate a DTrace DIF object that it is to be used as a helper. Helpers * are much more constrained than normal DIFOs. Specifically, they may * not: * * 1. Make calls to subroutines other than copyin(), copyinstr() or * miscellaneous string routines * 2. Access DTrace variables other than the args[] array, and the * curthread, pid, ppid, tid, execname, zonename, uid and gid variables. * 3. Have thread-local variables. * 4. Have dynamic variables. */ static int dtrace_difo_validate_helper(dtrace_difo_t *dp) { int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err; int err = 0; uint_t pc; for (pc = 0; pc < dp->dtdo_len; pc++) { dif_instr_t instr = dp->dtdo_buf[pc]; uint_t v = DIF_INSTR_VAR(instr); uint_t subr = DIF_INSTR_SUBR(instr); uint_t op = DIF_INSTR_OP(instr); switch (op) { case DIF_OP_OR: case DIF_OP_XOR: case DIF_OP_AND: case DIF_OP_SLL: case DIF_OP_SRL: case DIF_OP_SRA: case DIF_OP_SUB: case DIF_OP_ADD: case DIF_OP_MUL: case DIF_OP_SDIV: case DIF_OP_UDIV: case DIF_OP_SREM: case DIF_OP_UREM: case DIF_OP_COPYS: case DIF_OP_NOT: case DIF_OP_MOV: case DIF_OP_RLDSB: case DIF_OP_RLDSH: case DIF_OP_RLDSW: case DIF_OP_RLDUB: case DIF_OP_RLDUH: case DIF_OP_RLDUW: case DIF_OP_RLDX: case DIF_OP_ULDSB: case DIF_OP_ULDSH: case DIF_OP_ULDSW: case DIF_OP_ULDUB: case DIF_OP_ULDUH: case DIF_OP_ULDUW: case DIF_OP_ULDX: case DIF_OP_STB: case DIF_OP_STH: case DIF_OP_STW: case DIF_OP_STX: case DIF_OP_ALLOCS: case DIF_OP_CMP: case DIF_OP_SCMP: case DIF_OP_TST: case DIF_OP_BA: case DIF_OP_BE: case DIF_OP_BNE: case DIF_OP_BG: case DIF_OP_BGU: case DIF_OP_BGE: case DIF_OP_BGEU: case DIF_OP_BL: case DIF_OP_BLU: case DIF_OP_BLE: case DIF_OP_BLEU: case DIF_OP_RET: case DIF_OP_NOP: case DIF_OP_POPTS: case DIF_OP_FLUSHTS: case DIF_OP_SETX: case DIF_OP_SETS: case DIF_OP_LDGA: case DIF_OP_LDLS: case DIF_OP_STGS: case DIF_OP_STLS: case DIF_OP_PUSHTR: case DIF_OP_PUSHTV: break; case DIF_OP_LDGS: if (v >= DIF_VAR_OTHER_UBASE) break; if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9) break; if (v == DIF_VAR_CURTHREAD || v == DIF_VAR_PID || v == DIF_VAR_PPID || v == DIF_VAR_TID || v == DIF_VAR_EXECARGS || v == DIF_VAR_EXECNAME || v == DIF_VAR_ZONENAME || v == DIF_VAR_UID || v == DIF_VAR_GID) break; err += efunc(pc, "illegal variable %u\n", v); break; case DIF_OP_LDTA: case DIF_OP_LDTS: case DIF_OP_LDGAA: case DIF_OP_LDTAA: err += efunc(pc, "illegal dynamic variable load\n"); break; case DIF_OP_STTS: case DIF_OP_STGAA: case DIF_OP_STTAA: err += efunc(pc, "illegal dynamic variable store\n"); break; case DIF_OP_CALL: if (subr == DIF_SUBR_ALLOCA || subr == DIF_SUBR_BCOPY || subr == DIF_SUBR_COPYIN || subr == DIF_SUBR_COPYINTO || subr == DIF_SUBR_COPYINSTR || subr == DIF_SUBR_INDEX || subr == DIF_SUBR_INET_NTOA || subr == DIF_SUBR_INET_NTOA6 || subr == DIF_SUBR_INET_NTOP || subr == DIF_SUBR_JSON || subr == DIF_SUBR_LLTOSTR || subr == DIF_SUBR_STRTOLL || subr == DIF_SUBR_RINDEX || subr == DIF_SUBR_STRCHR || subr == DIF_SUBR_STRJOIN || subr == DIF_SUBR_STRRCHR || subr == DIF_SUBR_STRSTR || subr == DIF_SUBR_HTONS || subr == DIF_SUBR_HTONL || subr == DIF_SUBR_HTONLL || subr == DIF_SUBR_NTOHS || subr == DIF_SUBR_NTOHL || subr == DIF_SUBR_NTOHLL || subr == DIF_SUBR_MEMREF || #ifndef illumos subr == DIF_SUBR_MEMSTR || #endif subr == DIF_SUBR_TYPEREF) break; err += efunc(pc, "invalid subr %u\n", subr); break; default: err += efunc(pc, "invalid opcode %u\n", DIF_INSTR_OP(instr)); } } return (err); } /* * Returns 1 if the expression in the DIF object can be cached on a per-thread * basis; 0 if not. */ static int dtrace_difo_cacheable(dtrace_difo_t *dp) { int i; if (dp == NULL) return (0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_scope != DIFV_SCOPE_GLOBAL) continue; switch (v->dtdv_id) { case DIF_VAR_CURTHREAD: case DIF_VAR_PID: case DIF_VAR_TID: case DIF_VAR_EXECARGS: case DIF_VAR_EXECNAME: case DIF_VAR_ZONENAME: break; default: return (0); } } /* * This DIF object may be cacheable. Now we need to look for any * array loading instructions, any memory loading instructions, or * any stores to thread-local variables. */ for (i = 0; i < dp->dtdo_len; i++) { uint_t op = DIF_INSTR_OP(dp->dtdo_buf[i]); if ((op >= DIF_OP_LDSB && op <= DIF_OP_LDX) || (op >= DIF_OP_ULDSB && op <= DIF_OP_ULDX) || (op >= DIF_OP_RLDSB && op <= DIF_OP_RLDX) || op == DIF_OP_LDGA || op == DIF_OP_STTS) return (0); } return (1); } static void dtrace_difo_hold(dtrace_difo_t *dp) { int i; ASSERT(MUTEX_HELD(&dtrace_lock)); dp->dtdo_refcnt++; ASSERT(dp->dtdo_refcnt != 0); /* * We need to check this DIF object for references to the variable * DIF_VAR_VTIMESTAMP. */ for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id != DIF_VAR_VTIMESTAMP) continue; if (dtrace_vtime_references++ == 0) dtrace_vtime_enable(); } } /* * This routine calculates the dynamic variable chunksize for a given DIF * object. The calculation is not fool-proof, and can probably be tricked by * malicious DIF -- but it works for all compiler-generated DIF. Because this * calculation is likely imperfect, dtrace_dynvar() is able to gracefully fail * if a dynamic variable size exceeds the chunksize. */ static void dtrace_difo_chunksize(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { uint64_t sval = 0; dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */ const dif_instr_t *text = dp->dtdo_buf; uint_t pc, srd = 0; uint_t ttop = 0; size_t size, ksize; uint_t id, i; for (pc = 0; pc < dp->dtdo_len; pc++) { dif_instr_t instr = text[pc]; uint_t op = DIF_INSTR_OP(instr); uint_t rd = DIF_INSTR_RD(instr); uint_t r1 = DIF_INSTR_R1(instr); uint_t nkeys = 0; uchar_t scope = 0; dtrace_key_t *key = tupregs; switch (op) { case DIF_OP_SETX: sval = dp->dtdo_inttab[DIF_INSTR_INTEGER(instr)]; srd = rd; continue; case DIF_OP_STTS: key = &tupregs[DIF_DTR_NREGS]; key[0].dttk_size = 0; key[1].dttk_size = 0; nkeys = 2; scope = DIFV_SCOPE_THREAD; break; case DIF_OP_STGAA: case DIF_OP_STTAA: nkeys = ttop; if (DIF_INSTR_OP(instr) == DIF_OP_STTAA) key[nkeys++].dttk_size = 0; key[nkeys++].dttk_size = 0; if (op == DIF_OP_STTAA) { scope = DIFV_SCOPE_THREAD; } else { scope = DIFV_SCOPE_GLOBAL; } break; case DIF_OP_PUSHTR: if (ttop == DIF_DTR_NREGS) return; if ((srd == 0 || sval == 0) && r1 == DIF_TYPE_STRING) { /* * If the register for the size of the "pushtr" * is %r0 (or the value is 0) and the type is * a string, we'll use the system-wide default * string size. */ tupregs[ttop++].dttk_size = dtrace_strsize_default; } else { if (srd == 0) return; tupregs[ttop++].dttk_size = sval; } break; case DIF_OP_PUSHTV: if (ttop == DIF_DTR_NREGS) return; tupregs[ttop++].dttk_size = 0; break; case DIF_OP_FLUSHTS: ttop = 0; break; case DIF_OP_POPTS: if (ttop != 0) ttop--; break; } sval = 0; srd = 0; if (nkeys == 0) continue; /* * We have a dynamic variable allocation; calculate its size. */ for (ksize = 0, i = 0; i < nkeys; i++) ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t)); size = sizeof (dtrace_dynvar_t); size += sizeof (dtrace_key_t) * (nkeys - 1); size += ksize; /* * Now we need to determine the size of the stored data. */ id = DIF_INSTR_VAR(instr); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id == id && v->dtdv_scope == scope) { size += v->dtdv_type.dtdt_size; break; } } if (i == dp->dtdo_varlen) return; /* * We have the size. If this is larger than the chunk size * for our dynamic variable state, reset the chunk size. */ size = P2ROUNDUP(size, sizeof (uint64_t)); if (size > vstate->dtvs_dynvars.dtds_chunksize) vstate->dtvs_dynvars.dtds_chunksize = size; } } static void dtrace_difo_init(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i, oldsvars, osz, nsz, otlocals, ntlocals; uint_t id; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_buf != NULL && dp->dtdo_len != 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_statvar_t *svar, ***svarp = NULL; size_t dsize = 0; uint8_t scope = v->dtdv_scope; int *np = NULL; if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE) continue; id -= DIF_VAR_OTHER_UBASE; switch (scope) { case DIFV_SCOPE_THREAD: while (id >= (otlocals = vstate->dtvs_ntlocals)) { dtrace_difv_t *tlocals; if ((ntlocals = (otlocals << 1)) == 0) ntlocals = 1; osz = otlocals * sizeof (dtrace_difv_t); nsz = ntlocals * sizeof (dtrace_difv_t); tlocals = kmem_zalloc(nsz, KM_SLEEP); if (osz != 0) { bcopy(vstate->dtvs_tlocals, tlocals, osz); kmem_free(vstate->dtvs_tlocals, osz); } vstate->dtvs_tlocals = tlocals; vstate->dtvs_ntlocals = ntlocals; } vstate->dtvs_tlocals[id] = *v; continue; case DIFV_SCOPE_LOCAL: np = &vstate->dtvs_nlocals; svarp = &vstate->dtvs_locals; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) dsize = NCPU * (v->dtdv_type.dtdt_size + sizeof (uint64_t)); else dsize = NCPU * sizeof (uint64_t); break; case DIFV_SCOPE_GLOBAL: np = &vstate->dtvs_nglobals; svarp = &vstate->dtvs_globals; if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) dsize = v->dtdv_type.dtdt_size + sizeof (uint64_t); break; default: ASSERT(0); } while (id >= (oldsvars = *np)) { dtrace_statvar_t **statics; int newsvars, oldsize, newsize; if ((newsvars = (oldsvars << 1)) == 0) newsvars = 1; oldsize = oldsvars * sizeof (dtrace_statvar_t *); newsize = newsvars * sizeof (dtrace_statvar_t *); statics = kmem_zalloc(newsize, KM_SLEEP); if (oldsize != 0) { bcopy(*svarp, statics, oldsize); kmem_free(*svarp, oldsize); } *svarp = statics; *np = newsvars; } if ((svar = (*svarp)[id]) == NULL) { svar = kmem_zalloc(sizeof (dtrace_statvar_t), KM_SLEEP); svar->dtsv_var = *v; if ((svar->dtsv_size = dsize) != 0) { svar->dtsv_data = (uint64_t)(uintptr_t) kmem_zalloc(dsize, KM_SLEEP); } (*svarp)[id] = svar; } svar->dtsv_refcnt++; } dtrace_difo_chunksize(dp, vstate); dtrace_difo_hold(dp); } static dtrace_difo_t * dtrace_difo_duplicate(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { dtrace_difo_t *new; size_t sz; ASSERT(dp->dtdo_buf != NULL); ASSERT(dp->dtdo_refcnt != 0); new = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP); ASSERT(dp->dtdo_buf != NULL); sz = dp->dtdo_len * sizeof (dif_instr_t); new->dtdo_buf = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_buf, new->dtdo_buf, sz); new->dtdo_len = dp->dtdo_len; if (dp->dtdo_strtab != NULL) { ASSERT(dp->dtdo_strlen != 0); new->dtdo_strtab = kmem_alloc(dp->dtdo_strlen, KM_SLEEP); bcopy(dp->dtdo_strtab, new->dtdo_strtab, dp->dtdo_strlen); new->dtdo_strlen = dp->dtdo_strlen; } if (dp->dtdo_inttab != NULL) { ASSERT(dp->dtdo_intlen != 0); sz = dp->dtdo_intlen * sizeof (uint64_t); new->dtdo_inttab = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_inttab, new->dtdo_inttab, sz); new->dtdo_intlen = dp->dtdo_intlen; } if (dp->dtdo_vartab != NULL) { ASSERT(dp->dtdo_varlen != 0); sz = dp->dtdo_varlen * sizeof (dtrace_difv_t); new->dtdo_vartab = kmem_alloc(sz, KM_SLEEP); bcopy(dp->dtdo_vartab, new->dtdo_vartab, sz); new->dtdo_varlen = dp->dtdo_varlen; } dtrace_difo_init(new, vstate); return (new); } static void dtrace_difo_destroy(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i; ASSERT(dp->dtdo_refcnt == 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_statvar_t *svar, **svarp = NULL; uint_t id; uint8_t scope = v->dtdv_scope; int *np = NULL; switch (scope) { case DIFV_SCOPE_THREAD: continue; case DIFV_SCOPE_LOCAL: np = &vstate->dtvs_nlocals; svarp = vstate->dtvs_locals; break; case DIFV_SCOPE_GLOBAL: np = &vstate->dtvs_nglobals; svarp = vstate->dtvs_globals; break; default: ASSERT(0); } if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE) continue; id -= DIF_VAR_OTHER_UBASE; ASSERT(id < *np); svar = svarp[id]; ASSERT(svar != NULL); ASSERT(svar->dtsv_refcnt > 0); if (--svar->dtsv_refcnt > 0) continue; if (svar->dtsv_size != 0) { ASSERT(svar->dtsv_data != 0); kmem_free((void *)(uintptr_t)svar->dtsv_data, svar->dtsv_size); } kmem_free(svar, sizeof (dtrace_statvar_t)); svarp[id] = NULL; } if (dp->dtdo_buf != NULL) kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t)); if (dp->dtdo_inttab != NULL) kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t)); if (dp->dtdo_strtab != NULL) kmem_free(dp->dtdo_strtab, dp->dtdo_strlen); if (dp->dtdo_vartab != NULL) kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t)); kmem_free(dp, sizeof (dtrace_difo_t)); } static void dtrace_difo_release(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_refcnt != 0); for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; if (v->dtdv_id != DIF_VAR_VTIMESTAMP) continue; ASSERT(dtrace_vtime_references > 0); if (--dtrace_vtime_references == 0) dtrace_vtime_disable(); } if (--dp->dtdo_refcnt == 0) dtrace_difo_destroy(dp, vstate); } /* * DTrace Format Functions */ static uint16_t dtrace_format_add(dtrace_state_t *state, char *str) { char *fmt, **new; uint16_t ndx, len = strlen(str) + 1; fmt = kmem_zalloc(len, KM_SLEEP); bcopy(str, fmt, len); for (ndx = 0; ndx < state->dts_nformats; ndx++) { if (state->dts_formats[ndx] == NULL) { state->dts_formats[ndx] = fmt; return (ndx + 1); } } if (state->dts_nformats == USHRT_MAX) { /* * This is only likely if a denial-of-service attack is being * attempted. As such, it's okay to fail silently here. */ kmem_free(fmt, len); return (0); } /* * For simplicity, we always resize the formats array to be exactly the * number of formats. */ ndx = state->dts_nformats++; new = kmem_alloc((ndx + 1) * sizeof (char *), KM_SLEEP); if (state->dts_formats != NULL) { ASSERT(ndx != 0); bcopy(state->dts_formats, new, ndx * sizeof (char *)); kmem_free(state->dts_formats, ndx * sizeof (char *)); } state->dts_formats = new; state->dts_formats[ndx] = fmt; return (ndx + 1); } static void dtrace_format_remove(dtrace_state_t *state, uint16_t format) { char *fmt; ASSERT(state->dts_formats != NULL); ASSERT(format <= state->dts_nformats); ASSERT(state->dts_formats[format - 1] != NULL); fmt = state->dts_formats[format - 1]; kmem_free(fmt, strlen(fmt) + 1); state->dts_formats[format - 1] = NULL; } static void dtrace_format_destroy(dtrace_state_t *state) { int i; if (state->dts_nformats == 0) { ASSERT(state->dts_formats == NULL); return; } ASSERT(state->dts_formats != NULL); for (i = 0; i < state->dts_nformats; i++) { char *fmt = state->dts_formats[i]; if (fmt == NULL) continue; kmem_free(fmt, strlen(fmt) + 1); } kmem_free(state->dts_formats, state->dts_nformats * sizeof (char *)); state->dts_nformats = 0; state->dts_formats = NULL; } /* * DTrace Predicate Functions */ static dtrace_predicate_t * dtrace_predicate_create(dtrace_difo_t *dp) { dtrace_predicate_t *pred; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp->dtdo_refcnt != 0); pred = kmem_zalloc(sizeof (dtrace_predicate_t), KM_SLEEP); pred->dtp_difo = dp; pred->dtp_refcnt = 1; if (!dtrace_difo_cacheable(dp)) return (pred); if (dtrace_predcache_id == DTRACE_CACHEIDNONE) { /* * This is only theoretically possible -- we have had 2^32 * cacheable predicates on this machine. We cannot allow any * more predicates to become cacheable: as unlikely as it is, * there may be a thread caching a (now stale) predicate cache * ID. (N.B.: the temptation is being successfully resisted to * have this cmn_err() "Holy shit -- we executed this code!") */ return (pred); } pred->dtp_cacheid = dtrace_predcache_id++; return (pred); } static void dtrace_predicate_hold(dtrace_predicate_t *pred) { ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(pred->dtp_difo != NULL && pred->dtp_difo->dtdo_refcnt != 0); ASSERT(pred->dtp_refcnt > 0); pred->dtp_refcnt++; } static void dtrace_predicate_release(dtrace_predicate_t *pred, dtrace_vstate_t *vstate) { dtrace_difo_t *dp = pred->dtp_difo; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dp != NULL && dp->dtdo_refcnt != 0); ASSERT(pred->dtp_refcnt > 0); if (--pred->dtp_refcnt == 0) { dtrace_difo_release(pred->dtp_difo, vstate); kmem_free(pred, sizeof (dtrace_predicate_t)); } } /* * DTrace Action Description Functions */ static dtrace_actdesc_t * dtrace_actdesc_create(dtrace_actkind_t kind, uint32_t ntuple, uint64_t uarg, uint64_t arg) { dtrace_actdesc_t *act; #ifdef illumos ASSERT(!DTRACEACT_ISPRINTFLIKE(kind) || (arg != NULL && arg >= KERNELBASE) || (arg == NULL && kind == DTRACEACT_PRINTA)); #endif act = kmem_zalloc(sizeof (dtrace_actdesc_t), KM_SLEEP); act->dtad_kind = kind; act->dtad_ntuple = ntuple; act->dtad_uarg = uarg; act->dtad_arg = arg; act->dtad_refcnt = 1; return (act); } static void dtrace_actdesc_hold(dtrace_actdesc_t *act) { ASSERT(act->dtad_refcnt >= 1); act->dtad_refcnt++; } static void dtrace_actdesc_release(dtrace_actdesc_t *act, dtrace_vstate_t *vstate) { dtrace_actkind_t kind = act->dtad_kind; dtrace_difo_t *dp; ASSERT(act->dtad_refcnt >= 1); if (--act->dtad_refcnt != 0) return; if ((dp = act->dtad_difo) != NULL) dtrace_difo_release(dp, vstate); if (DTRACEACT_ISPRINTFLIKE(kind)) { char *str = (char *)(uintptr_t)act->dtad_arg; #ifdef illumos ASSERT((str != NULL && (uintptr_t)str >= KERNELBASE) || (str == NULL && act->dtad_kind == DTRACEACT_PRINTA)); #endif if (str != NULL) kmem_free(str, strlen(str) + 1); } kmem_free(act, sizeof (dtrace_actdesc_t)); } /* * DTrace ECB Functions */ static dtrace_ecb_t * dtrace_ecb_add(dtrace_state_t *state, dtrace_probe_t *probe) { dtrace_ecb_t *ecb; dtrace_epid_t epid; ASSERT(MUTEX_HELD(&dtrace_lock)); ecb = kmem_zalloc(sizeof (dtrace_ecb_t), KM_SLEEP); ecb->dte_predicate = NULL; ecb->dte_probe = probe; /* * The default size is the size of the default action: recording * the header. */ ecb->dte_size = ecb->dte_needed = sizeof (dtrace_rechdr_t); ecb->dte_alignment = sizeof (dtrace_epid_t); epid = state->dts_epid++; if (epid - 1 >= state->dts_necbs) { dtrace_ecb_t **oecbs = state->dts_ecbs, **ecbs; int necbs = state->dts_necbs << 1; ASSERT(epid == state->dts_necbs + 1); if (necbs == 0) { ASSERT(oecbs == NULL); necbs = 1; } ecbs = kmem_zalloc(necbs * sizeof (*ecbs), KM_SLEEP); if (oecbs != NULL) bcopy(oecbs, ecbs, state->dts_necbs * sizeof (*ecbs)); dtrace_membar_producer(); state->dts_ecbs = ecbs; if (oecbs != NULL) { /* * If this state is active, we must dtrace_sync() * before we can free the old dts_ecbs array: we're * coming in hot, and there may be active ring * buffer processing (which indexes into the dts_ecbs * array) on another CPU. */ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) dtrace_sync(); kmem_free(oecbs, state->dts_necbs * sizeof (*ecbs)); } dtrace_membar_producer(); state->dts_necbs = necbs; } ecb->dte_state = state; ASSERT(state->dts_ecbs[epid - 1] == NULL); dtrace_membar_producer(); state->dts_ecbs[(ecb->dte_epid = epid) - 1] = ecb; return (ecb); } static void dtrace_ecb_enable(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_next == NULL); if (probe == NULL) { /* * This is the NULL probe -- there's nothing to do. */ return; } if (probe->dtpr_ecb == NULL) { dtrace_provider_t *prov = probe->dtpr_provider; /* * We're the first ECB on this probe. */ probe->dtpr_ecb = probe->dtpr_ecb_last = ecb; if (ecb->dte_predicate != NULL) probe->dtpr_predcache = ecb->dte_predicate->dtp_cacheid; prov->dtpv_pops.dtps_enable(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); } else { /* * This probe is already active. Swing the last pointer to * point to the new ECB, and issue a dtrace_sync() to assure * that all CPUs have seen the change. */ ASSERT(probe->dtpr_ecb_last != NULL); probe->dtpr_ecb_last->dte_next = ecb; probe->dtpr_ecb_last = ecb; probe->dtpr_predcache = 0; dtrace_sync(); } } static void dtrace_ecb_resize(dtrace_ecb_t *ecb) { dtrace_action_t *act; uint32_t curneeded = UINT32_MAX; uint32_t aggbase = UINT32_MAX; /* * If we record anything, we always record the dtrace_rechdr_t. (And * we always record it first.) */ ecb->dte_size = sizeof (dtrace_rechdr_t); ecb->dte_alignment = sizeof (dtrace_epid_t); for (act = ecb->dte_action; act != NULL; act = act->dta_next) { dtrace_recdesc_t *rec = &act->dta_rec; ASSERT(rec->dtrd_size > 0 || rec->dtrd_alignment == 1); ecb->dte_alignment = MAX(ecb->dte_alignment, rec->dtrd_alignment); if (DTRACEACT_ISAGG(act->dta_kind)) { dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act; ASSERT(rec->dtrd_size != 0); ASSERT(agg->dtag_first != NULL); ASSERT(act->dta_prev->dta_intuple); ASSERT(aggbase != UINT32_MAX); ASSERT(curneeded != UINT32_MAX); agg->dtag_base = aggbase; curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); rec->dtrd_offset = curneeded; curneeded += rec->dtrd_size; ecb->dte_needed = MAX(ecb->dte_needed, curneeded); aggbase = UINT32_MAX; curneeded = UINT32_MAX; } else if (act->dta_intuple) { if (curneeded == UINT32_MAX) { /* * This is the first record in a tuple. Align * curneeded to be at offset 4 in an 8-byte * aligned block. */ ASSERT(act->dta_prev == NULL || !act->dta_prev->dta_intuple); ASSERT3U(aggbase, ==, UINT32_MAX); curneeded = P2PHASEUP(ecb->dte_size, sizeof (uint64_t), sizeof (dtrace_aggid_t)); aggbase = curneeded - sizeof (dtrace_aggid_t); ASSERT(IS_P2ALIGNED(aggbase, sizeof (uint64_t))); } curneeded = P2ROUNDUP(curneeded, rec->dtrd_alignment); rec->dtrd_offset = curneeded; curneeded += rec->dtrd_size; } else { /* tuples must be followed by an aggregation */ ASSERT(act->dta_prev == NULL || !act->dta_prev->dta_intuple); ecb->dte_size = P2ROUNDUP(ecb->dte_size, rec->dtrd_alignment); rec->dtrd_offset = ecb->dte_size; ecb->dte_size += rec->dtrd_size; ecb->dte_needed = MAX(ecb->dte_needed, ecb->dte_size); } } if ((act = ecb->dte_action) != NULL && !(act->dta_kind == DTRACEACT_SPECULATE && act->dta_next == NULL) && ecb->dte_size == sizeof (dtrace_rechdr_t)) { /* * If the size is still sizeof (dtrace_rechdr_t), then all * actions store no data; set the size to 0. */ ecb->dte_size = 0; } ecb->dte_size = P2ROUNDUP(ecb->dte_size, sizeof (dtrace_epid_t)); ecb->dte_needed = P2ROUNDUP(ecb->dte_needed, (sizeof (dtrace_epid_t))); ecb->dte_state->dts_needed = MAX(ecb->dte_state->dts_needed, ecb->dte_needed); } static dtrace_action_t * dtrace_ecb_aggregation_create(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc) { dtrace_aggregation_t *agg; size_t size = sizeof (uint64_t); int ntuple = desc->dtad_ntuple; dtrace_action_t *act; dtrace_recdesc_t *frec; dtrace_aggid_t aggid; dtrace_state_t *state = ecb->dte_state; agg = kmem_zalloc(sizeof (dtrace_aggregation_t), KM_SLEEP); agg->dtag_ecb = ecb; ASSERT(DTRACEACT_ISAGG(desc->dtad_kind)); switch (desc->dtad_kind) { case DTRACEAGG_MIN: agg->dtag_initial = INT64_MAX; agg->dtag_aggregate = dtrace_aggregate_min; break; case DTRACEAGG_MAX: agg->dtag_initial = INT64_MIN; agg->dtag_aggregate = dtrace_aggregate_max; break; case DTRACEAGG_COUNT: agg->dtag_aggregate = dtrace_aggregate_count; break; case DTRACEAGG_QUANTIZE: agg->dtag_aggregate = dtrace_aggregate_quantize; size = (((sizeof (uint64_t) * NBBY) - 1) * 2 + 1) * sizeof (uint64_t); break; case DTRACEAGG_LQUANTIZE: { uint16_t step = DTRACE_LQUANTIZE_STEP(desc->dtad_arg); uint16_t levels = DTRACE_LQUANTIZE_LEVELS(desc->dtad_arg); agg->dtag_initial = desc->dtad_arg; agg->dtag_aggregate = dtrace_aggregate_lquantize; if (step == 0 || levels == 0) goto err; size = levels * sizeof (uint64_t) + 3 * sizeof (uint64_t); break; } case DTRACEAGG_LLQUANTIZE: { uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(desc->dtad_arg); uint16_t low = DTRACE_LLQUANTIZE_LOW(desc->dtad_arg); uint16_t high = DTRACE_LLQUANTIZE_HIGH(desc->dtad_arg); uint16_t nsteps = DTRACE_LLQUANTIZE_NSTEP(desc->dtad_arg); int64_t v; agg->dtag_initial = desc->dtad_arg; agg->dtag_aggregate = dtrace_aggregate_llquantize; if (factor < 2 || low >= high || nsteps < factor) goto err; /* * Now check that the number of steps evenly divides a power * of the factor. (This assures both integer bucket size and * linearity within each magnitude.) */ for (v = factor; v < nsteps; v *= factor) continue; if ((v % nsteps) || (nsteps % factor)) goto err; size = (dtrace_aggregate_llquantize_bucket(factor, low, high, nsteps, INT64_MAX) + 2) * sizeof (uint64_t); break; } case DTRACEAGG_AVG: agg->dtag_aggregate = dtrace_aggregate_avg; size = sizeof (uint64_t) * 2; break; case DTRACEAGG_STDDEV: agg->dtag_aggregate = dtrace_aggregate_stddev; size = sizeof (uint64_t) * 4; break; case DTRACEAGG_SUM: agg->dtag_aggregate = dtrace_aggregate_sum; break; default: goto err; } agg->dtag_action.dta_rec.dtrd_size = size; if (ntuple == 0) goto err; /* * We must make sure that we have enough actions for the n-tuple. */ for (act = ecb->dte_action_last; act != NULL; act = act->dta_prev) { if (DTRACEACT_ISAGG(act->dta_kind)) break; if (--ntuple == 0) { /* * This is the action with which our n-tuple begins. */ agg->dtag_first = act; goto success; } } /* * This n-tuple is short by ntuple elements. Return failure. */ ASSERT(ntuple != 0); err: kmem_free(agg, sizeof (dtrace_aggregation_t)); return (NULL); success: /* * If the last action in the tuple has a size of zero, it's actually * an expression argument for the aggregating action. */ ASSERT(ecb->dte_action_last != NULL); act = ecb->dte_action_last; if (act->dta_kind == DTRACEACT_DIFEXPR) { ASSERT(act->dta_difo != NULL); if (act->dta_difo->dtdo_rtype.dtdt_size == 0) agg->dtag_hasarg = 1; } /* * We need to allocate an id for this aggregation. */ #ifdef illumos aggid = (dtrace_aggid_t)(uintptr_t)vmem_alloc(state->dts_aggid_arena, 1, VM_BESTFIT | VM_SLEEP); #else aggid = alloc_unr(state->dts_aggid_arena); #endif if (aggid - 1 >= state->dts_naggregations) { dtrace_aggregation_t **oaggs = state->dts_aggregations; dtrace_aggregation_t **aggs; int naggs = state->dts_naggregations << 1; int onaggs = state->dts_naggregations; ASSERT(aggid == state->dts_naggregations + 1); if (naggs == 0) { ASSERT(oaggs == NULL); naggs = 1; } aggs = kmem_zalloc(naggs * sizeof (*aggs), KM_SLEEP); if (oaggs != NULL) { bcopy(oaggs, aggs, onaggs * sizeof (*aggs)); kmem_free(oaggs, onaggs * sizeof (*aggs)); } state->dts_aggregations = aggs; state->dts_naggregations = naggs; } ASSERT(state->dts_aggregations[aggid - 1] == NULL); state->dts_aggregations[(agg->dtag_id = aggid) - 1] = agg; frec = &agg->dtag_first->dta_rec; if (frec->dtrd_alignment < sizeof (dtrace_aggid_t)) frec->dtrd_alignment = sizeof (dtrace_aggid_t); for (act = agg->dtag_first; act != NULL; act = act->dta_next) { ASSERT(!act->dta_intuple); act->dta_intuple = 1; } return (&agg->dtag_action); } static void dtrace_ecb_aggregation_destroy(dtrace_ecb_t *ecb, dtrace_action_t *act) { dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act; dtrace_state_t *state = ecb->dte_state; dtrace_aggid_t aggid = agg->dtag_id; ASSERT(DTRACEACT_ISAGG(act->dta_kind)); #ifdef illumos vmem_free(state->dts_aggid_arena, (void *)(uintptr_t)aggid, 1); #else free_unr(state->dts_aggid_arena, aggid); #endif ASSERT(state->dts_aggregations[aggid - 1] == agg); state->dts_aggregations[aggid - 1] = NULL; kmem_free(agg, sizeof (dtrace_aggregation_t)); } static int dtrace_ecb_action_add(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc) { dtrace_action_t *action, *last; dtrace_difo_t *dp = desc->dtad_difo; uint32_t size = 0, align = sizeof (uint8_t), mask; uint16_t format = 0; dtrace_recdesc_t *rec; dtrace_state_t *state = ecb->dte_state; dtrace_optval_t *opt = state->dts_options, nframes = 0, strsize; uint64_t arg = desc->dtad_arg; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_action == NULL || ecb->dte_action->dta_refcnt == 1); if (DTRACEACT_ISAGG(desc->dtad_kind)) { /* * If this is an aggregating action, there must be neither * a speculate nor a commit on the action chain. */ dtrace_action_t *act; for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); if (act->dta_kind == DTRACEACT_SPECULATE) return (EINVAL); } action = dtrace_ecb_aggregation_create(ecb, desc); if (action == NULL) return (EINVAL); } else { if (DTRACEACT_ISDESTRUCTIVE(desc->dtad_kind) || (desc->dtad_kind == DTRACEACT_DIFEXPR && dp != NULL && dp->dtdo_destructive)) { state->dts_destructive = 1; } switch (desc->dtad_kind) { case DTRACEACT_PRINTF: case DTRACEACT_PRINTA: case DTRACEACT_SYSTEM: case DTRACEACT_FREOPEN: case DTRACEACT_DIFEXPR: /* * We know that our arg is a string -- turn it into a * format. */ if (arg == 0) { ASSERT(desc->dtad_kind == DTRACEACT_PRINTA || desc->dtad_kind == DTRACEACT_DIFEXPR); format = 0; } else { ASSERT(arg != 0); #ifdef illumos ASSERT(arg > KERNELBASE); #endif format = dtrace_format_add(state, (char *)(uintptr_t)arg); } /*FALLTHROUGH*/ case DTRACEACT_LIBACT: case DTRACEACT_TRACEMEM: case DTRACEACT_TRACEMEM_DYNSIZE: if (dp == NULL) return (EINVAL); if ((size = dp->dtdo_rtype.dtdt_size) != 0) break; if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) { if (!(dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); size = opt[DTRACEOPT_STRSIZE]; } break; case DTRACEACT_STACK: if ((nframes = arg) == 0) { nframes = opt[DTRACEOPT_STACKFRAMES]; ASSERT(nframes > 0); arg = nframes; } size = nframes * sizeof (pc_t); break; case DTRACEACT_JSTACK: if ((strsize = DTRACE_USTACK_STRSIZE(arg)) == 0) strsize = opt[DTRACEOPT_JSTACKSTRSIZE]; if ((nframes = DTRACE_USTACK_NFRAMES(arg)) == 0) nframes = opt[DTRACEOPT_JSTACKFRAMES]; arg = DTRACE_USTACK_ARG(nframes, strsize); /*FALLTHROUGH*/ case DTRACEACT_USTACK: if (desc->dtad_kind != DTRACEACT_JSTACK && (nframes = DTRACE_USTACK_NFRAMES(arg)) == 0) { strsize = DTRACE_USTACK_STRSIZE(arg); nframes = opt[DTRACEOPT_USTACKFRAMES]; ASSERT(nframes > 0); arg = DTRACE_USTACK_ARG(nframes, strsize); } /* * Save a slot for the pid. */ size = (nframes + 1) * sizeof (uint64_t); size += DTRACE_USTACK_STRSIZE(arg); size = P2ROUNDUP(size, (uint32_t)(sizeof (uintptr_t))); break; case DTRACEACT_SYM: case DTRACEACT_MOD: if (dp == NULL || ((size = dp->dtdo_rtype.dtdt_size) != sizeof (uint64_t)) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); break; case DTRACEACT_USYM: case DTRACEACT_UMOD: case DTRACEACT_UADDR: if (dp == NULL || (dp->dtdo_rtype.dtdt_size != sizeof (uint64_t)) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); /* * We have a slot for the pid, plus a slot for the * argument. To keep things simple (aligned with * bitness-neutral sizing), we store each as a 64-bit * quantity. */ size = 2 * sizeof (uint64_t); break; case DTRACEACT_STOP: case DTRACEACT_BREAKPOINT: case DTRACEACT_PANIC: break; case DTRACEACT_CHILL: case DTRACEACT_DISCARD: case DTRACEACT_RAISE: if (dp == NULL) return (EINVAL); break; case DTRACEACT_EXIT: if (dp == NULL || (size = dp->dtdo_rtype.dtdt_size) != sizeof (int) || (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) return (EINVAL); break; case DTRACEACT_SPECULATE: if (ecb->dte_size > sizeof (dtrace_rechdr_t)) return (EINVAL); if (dp == NULL) return (EINVAL); state->dts_speculates = 1; break; case DTRACEACT_PRINTM: size = dp->dtdo_rtype.dtdt_size; break; case DTRACEACT_PRINTT: size = dp->dtdo_rtype.dtdt_size; break; case DTRACEACT_COMMIT: { dtrace_action_t *act = ecb->dte_action; for (; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); } if (dp == NULL) return (EINVAL); break; } default: return (EINVAL); } if (size != 0 || desc->dtad_kind == DTRACEACT_SPECULATE) { /* * If this is a data-storing action or a speculate, * we must be sure that there isn't a commit on the * action chain. */ dtrace_action_t *act = ecb->dte_action; for (; act != NULL; act = act->dta_next) { if (act->dta_kind == DTRACEACT_COMMIT) return (EINVAL); } } action = kmem_zalloc(sizeof (dtrace_action_t), KM_SLEEP); action->dta_rec.dtrd_size = size; } action->dta_refcnt = 1; rec = &action->dta_rec; size = rec->dtrd_size; for (mask = sizeof (uint64_t) - 1; size != 0 && mask > 0; mask >>= 1) { if (!(size & mask)) { align = mask + 1; break; } } action->dta_kind = desc->dtad_kind; if ((action->dta_difo = dp) != NULL) dtrace_difo_hold(dp); rec->dtrd_action = action->dta_kind; rec->dtrd_arg = arg; rec->dtrd_uarg = desc->dtad_uarg; rec->dtrd_alignment = (uint16_t)align; rec->dtrd_format = format; if ((last = ecb->dte_action_last) != NULL) { ASSERT(ecb->dte_action != NULL); action->dta_prev = last; last->dta_next = action; } else { ASSERT(ecb->dte_action == NULL); ecb->dte_action = action; } ecb->dte_action_last = action; return (0); } static void dtrace_ecb_action_remove(dtrace_ecb_t *ecb) { dtrace_action_t *act = ecb->dte_action, *next; dtrace_vstate_t *vstate = &ecb->dte_state->dts_vstate; dtrace_difo_t *dp; uint16_t format; if (act != NULL && act->dta_refcnt > 1) { ASSERT(act->dta_next == NULL || act->dta_next->dta_refcnt == 1); act->dta_refcnt--; } else { for (; act != NULL; act = next) { next = act->dta_next; ASSERT(next != NULL || act == ecb->dte_action_last); ASSERT(act->dta_refcnt == 1); if ((format = act->dta_rec.dtrd_format) != 0) dtrace_format_remove(ecb->dte_state, format); if ((dp = act->dta_difo) != NULL) dtrace_difo_release(dp, vstate); if (DTRACEACT_ISAGG(act->dta_kind)) { dtrace_ecb_aggregation_destroy(ecb, act); } else { kmem_free(act, sizeof (dtrace_action_t)); } } } ecb->dte_action = NULL; ecb->dte_action_last = NULL; ecb->dte_size = 0; } static void dtrace_ecb_disable(dtrace_ecb_t *ecb) { /* * We disable the ECB by removing it from its probe. */ dtrace_ecb_t *pecb, *prev = NULL; dtrace_probe_t *probe = ecb->dte_probe; ASSERT(MUTEX_HELD(&dtrace_lock)); if (probe == NULL) { /* * This is the NULL probe; there is nothing to disable. */ return; } for (pecb = probe->dtpr_ecb; pecb != NULL; pecb = pecb->dte_next) { if (pecb == ecb) break; prev = pecb; } ASSERT(pecb != NULL); if (prev == NULL) { probe->dtpr_ecb = ecb->dte_next; } else { prev->dte_next = ecb->dte_next; } if (ecb == probe->dtpr_ecb_last) { ASSERT(ecb->dte_next == NULL); probe->dtpr_ecb_last = prev; } /* * The ECB has been disconnected from the probe; now sync to assure * that all CPUs have seen the change before returning. */ dtrace_sync(); if (probe->dtpr_ecb == NULL) { /* * That was the last ECB on the probe; clear the predicate * cache ID for the probe, disable it and sync one more time * to assure that we'll never hit it again. */ dtrace_provider_t *prov = probe->dtpr_provider; ASSERT(ecb->dte_next == NULL); ASSERT(probe->dtpr_ecb_last == NULL); probe->dtpr_predcache = DTRACE_CACHEIDNONE; prov->dtpv_pops.dtps_disable(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); dtrace_sync(); } else { /* * There is at least one ECB remaining on the probe. If there * is _exactly_ one, set the probe's predicate cache ID to be * the predicate cache ID of the remaining ECB. */ ASSERT(probe->dtpr_ecb_last != NULL); ASSERT(probe->dtpr_predcache == DTRACE_CACHEIDNONE); if (probe->dtpr_ecb == probe->dtpr_ecb_last) { dtrace_predicate_t *p = probe->dtpr_ecb->dte_predicate; ASSERT(probe->dtpr_ecb->dte_next == NULL); if (p != NULL) probe->dtpr_predcache = p->dtp_cacheid; } ecb->dte_next = NULL; } } static void dtrace_ecb_destroy(dtrace_ecb_t *ecb) { dtrace_state_t *state = ecb->dte_state; dtrace_vstate_t *vstate = &state->dts_vstate; dtrace_predicate_t *pred; dtrace_epid_t epid = ecb->dte_epid; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(ecb->dte_next == NULL); ASSERT(ecb->dte_probe == NULL || ecb->dte_probe->dtpr_ecb != ecb); if ((pred = ecb->dte_predicate) != NULL) dtrace_predicate_release(pred, vstate); dtrace_ecb_action_remove(ecb); ASSERT(state->dts_ecbs[epid - 1] == ecb); state->dts_ecbs[epid - 1] = NULL; kmem_free(ecb, sizeof (dtrace_ecb_t)); } static dtrace_ecb_t * dtrace_ecb_create(dtrace_state_t *state, dtrace_probe_t *probe, dtrace_enabling_t *enab) { dtrace_ecb_t *ecb; dtrace_predicate_t *pred; dtrace_actdesc_t *act; dtrace_provider_t *prov; dtrace_ecbdesc_t *desc = enab->dten_current; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(state != NULL); ecb = dtrace_ecb_add(state, probe); ecb->dte_uarg = desc->dted_uarg; if ((pred = desc->dted_pred.dtpdd_predicate) != NULL) { dtrace_predicate_hold(pred); ecb->dte_predicate = pred; } if (probe != NULL) { /* * If the provider shows more leg than the consumer is old * enough to see, we need to enable the appropriate implicit * predicate bits to prevent the ecb from activating at * revealing times. * * Providers specifying DTRACE_PRIV_USER at register time * are stating that they need the /proc-style privilege * model to be enforced, and this is what DTRACE_COND_OWNER * and DTRACE_COND_ZONEOWNER will then do at probe time. */ prov = probe->dtpr_provider; if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLPROC) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER)) ecb->dte_cond |= DTRACE_COND_OWNER; if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLZONE) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER)) ecb->dte_cond |= DTRACE_COND_ZONEOWNER; /* * If the provider shows us kernel innards and the user * is lacking sufficient privilege, enable the * DTRACE_COND_USERMODE implicit predicate. */ if (!(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) && (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_KERNEL)) ecb->dte_cond |= DTRACE_COND_USERMODE; } if (dtrace_ecb_create_cache != NULL) { /* * If we have a cached ecb, we'll use its action list instead * of creating our own (saving both time and space). */ dtrace_ecb_t *cached = dtrace_ecb_create_cache; dtrace_action_t *act = cached->dte_action; if (act != NULL) { ASSERT(act->dta_refcnt > 0); act->dta_refcnt++; ecb->dte_action = act; ecb->dte_action_last = cached->dte_action_last; ecb->dte_needed = cached->dte_needed; ecb->dte_size = cached->dte_size; ecb->dte_alignment = cached->dte_alignment; } return (ecb); } for (act = desc->dted_action; act != NULL; act = act->dtad_next) { if ((enab->dten_error = dtrace_ecb_action_add(ecb, act)) != 0) { dtrace_ecb_destroy(ecb); return (NULL); } } dtrace_ecb_resize(ecb); return (dtrace_ecb_create_cache = ecb); } static int dtrace_ecb_create_enable(dtrace_probe_t *probe, void *arg) { dtrace_ecb_t *ecb; dtrace_enabling_t *enab = arg; dtrace_state_t *state = enab->dten_vstate->dtvs_state; ASSERT(state != NULL); if (probe != NULL && probe->dtpr_gen < enab->dten_probegen) { /* * This probe was created in a generation for which this * enabling has previously created ECBs; we don't want to * enable it again, so just kick out. */ return (DTRACE_MATCH_NEXT); } if ((ecb = dtrace_ecb_create(state, probe, enab)) == NULL) return (DTRACE_MATCH_DONE); dtrace_ecb_enable(ecb); return (DTRACE_MATCH_NEXT); } static dtrace_ecb_t * dtrace_epid2ecb(dtrace_state_t *state, dtrace_epid_t id) { dtrace_ecb_t *ecb; ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > state->dts_necbs) return (NULL); ASSERT(state->dts_necbs > 0 && state->dts_ecbs != NULL); ASSERT((ecb = state->dts_ecbs[id - 1]) == NULL || ecb->dte_epid == id); return (state->dts_ecbs[id - 1]); } static dtrace_aggregation_t * dtrace_aggid2agg(dtrace_state_t *state, dtrace_aggid_t id) { dtrace_aggregation_t *agg; ASSERT(MUTEX_HELD(&dtrace_lock)); if (id == 0 || id > state->dts_naggregations) return (NULL); ASSERT(state->dts_naggregations > 0 && state->dts_aggregations != NULL); ASSERT((agg = state->dts_aggregations[id - 1]) == NULL || agg->dtag_id == id); return (state->dts_aggregations[id - 1]); } /* * DTrace Buffer Functions * * The following functions manipulate DTrace buffers. Most of these functions * are called in the context of establishing or processing consumer state; * exceptions are explicitly noted. */ /* * Note: called from cross call context. This function switches the two * buffers on a given CPU. The atomicity of this operation is assured by * disabling interrupts while the actual switch takes place; the disabling of * interrupts serializes the execution with any execution of dtrace_probe() on * the same CPU. */ static void dtrace_buffer_switch(dtrace_buffer_t *buf) { caddr_t tomax = buf->dtb_tomax; caddr_t xamot = buf->dtb_xamot; dtrace_icookie_t cookie; hrtime_t now; ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); ASSERT(!(buf->dtb_flags & DTRACEBUF_RING)); cookie = dtrace_interrupt_disable(); now = dtrace_gethrtime(); buf->dtb_tomax = xamot; buf->dtb_xamot = tomax; buf->dtb_xamot_drops = buf->dtb_drops; buf->dtb_xamot_offset = buf->dtb_offset; buf->dtb_xamot_errors = buf->dtb_errors; buf->dtb_xamot_flags = buf->dtb_flags; buf->dtb_offset = 0; buf->dtb_drops = 0; buf->dtb_errors = 0; buf->dtb_flags &= ~(DTRACEBUF_ERROR | DTRACEBUF_DROPPED); buf->dtb_interval = now - buf->dtb_switched; buf->dtb_switched = now; dtrace_interrupt_enable(cookie); } /* * Note: called from cross call context. This function activates a buffer * on a CPU. As with dtrace_buffer_switch(), the atomicity of the operation * is guaranteed by the disabling of interrupts. */ static void dtrace_buffer_activate(dtrace_state_t *state) { dtrace_buffer_t *buf; dtrace_icookie_t cookie = dtrace_interrupt_disable(); buf = &state->dts_buffer[curcpu]; if (buf->dtb_tomax != NULL) { /* * We might like to assert that the buffer is marked inactive, * but this isn't necessarily true: the buffer for the CPU * that processes the BEGIN probe has its buffer activated * manually. In this case, we take the (harmless) action * re-clearing the bit INACTIVE bit. */ buf->dtb_flags &= ~DTRACEBUF_INACTIVE; } dtrace_interrupt_enable(cookie); } static int dtrace_buffer_alloc(dtrace_buffer_t *bufs, size_t size, int flags, processorid_t cpu, int *factor) { #ifdef illumos cpu_t *cp; #endif dtrace_buffer_t *buf; int allocated = 0, desired = 0; #ifdef illumos ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); *factor = 1; if (size > dtrace_nonroot_maxsize && !PRIV_POLICY_CHOICE(CRED(), PRIV_ALL, B_FALSE)) return (EFBIG); cp = cpu_list; do { if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id) continue; buf = &bufs[cp->cpu_id]; /* * If there is already a buffer allocated for this CPU, it * is only possible that this is a DR event. In this case, */ if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); continue; } ASSERT(buf->dtb_xamot == NULL); if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; buf->dtb_size = size; buf->dtb_flags = flags; buf->dtb_offset = 0; buf->dtb_drops = 0; if (flags & DTRACEBUF_NOSWITCH) continue; if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; } while ((cp = cp->cpu_next) != cpu_list); return (0); err: cp = cpu_list; do { if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id) continue; buf = &bufs[cp->cpu_id]; desired += 2; if (buf->dtb_xamot != NULL) { ASSERT(buf->dtb_tomax != NULL); ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_xamot, size); allocated++; } if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_tomax, size); allocated++; } buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; buf->dtb_size = 0; } while ((cp = cp->cpu_next) != cpu_list); #else int i; *factor = 1; #if defined(__amd64__) || defined(__mips__) || defined(__powerpc__) /* * FreeBSD isn't good at limiting the amount of memory we * ask to malloc, so let's place a limit here before trying * to do something that might well end in tears at bedtime. */ if (size > physmem * PAGE_SIZE / (128 * (mp_maxid + 1))) return (ENOMEM); #endif ASSERT(MUTEX_HELD(&dtrace_lock)); CPU_FOREACH(i) { if (cpu != DTRACE_CPUALL && cpu != i) continue; buf = &bufs[i]; /* * If there is already a buffer allocated for this CPU, it * is only possible that this is a DR event. In this case, * the buffer size must match our specified size. */ if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); continue; } ASSERT(buf->dtb_xamot == NULL); if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; buf->dtb_size = size; buf->dtb_flags = flags; buf->dtb_offset = 0; buf->dtb_drops = 0; if (flags & DTRACEBUF_NOSWITCH) continue; if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) goto err; } return (0); err: /* * Error allocating memory, so free the buffers that were * allocated before the failed allocation. */ CPU_FOREACH(i) { if (cpu != DTRACE_CPUALL && cpu != i) continue; buf = &bufs[i]; desired += 2; if (buf->dtb_xamot != NULL) { ASSERT(buf->dtb_tomax != NULL); ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_xamot, size); allocated++; } if (buf->dtb_tomax != NULL) { ASSERT(buf->dtb_size == size); kmem_free(buf->dtb_tomax, size); allocated++; } buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; buf->dtb_size = 0; } #endif *factor = desired / (allocated > 0 ? allocated : 1); return (ENOMEM); } /* * Note: called from probe context. This function just increments the drop * count on a buffer. It has been made a function to allow for the * possibility of understanding the source of mysterious drop counts. (A * problem for which one may be particularly disappointed that DTrace cannot * be used to understand DTrace.) */ static void dtrace_buffer_drop(dtrace_buffer_t *buf) { buf->dtb_drops++; } /* * Note: called from probe context. This function is called to reserve space * in a buffer. If mstate is non-NULL, sets the scratch base and size in the * mstate. Returns the new offset in the buffer, or a negative value if an * error has occurred. */ static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *buf, size_t needed, size_t align, dtrace_state_t *state, dtrace_mstate_t *mstate) { intptr_t offs = buf->dtb_offset, soffs; intptr_t woffs; caddr_t tomax; size_t total; if (buf->dtb_flags & DTRACEBUF_INACTIVE) return (-1); if ((tomax = buf->dtb_tomax) == NULL) { dtrace_buffer_drop(buf); return (-1); } if (!(buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL))) { while (offs & (align - 1)) { /* * Assert that our alignment is off by a number which * is itself sizeof (uint32_t) aligned. */ ASSERT(!((align - (offs & (align - 1))) & (sizeof (uint32_t) - 1))); DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE); offs += sizeof (uint32_t); } if ((soffs = offs + needed) > buf->dtb_size) { dtrace_buffer_drop(buf); return (-1); } if (mstate == NULL) return (offs); mstate->dtms_scratch_base = (uintptr_t)tomax + soffs; mstate->dtms_scratch_size = buf->dtb_size - soffs; mstate->dtms_scratch_ptr = mstate->dtms_scratch_base; return (offs); } if (buf->dtb_flags & DTRACEBUF_FILL) { if (state->dts_activity != DTRACE_ACTIVITY_COOLDOWN && (buf->dtb_flags & DTRACEBUF_FULL)) return (-1); goto out; } total = needed + (offs & (align - 1)); /* * For a ring buffer, life is quite a bit more complicated. Before * we can store any padding, we need to adjust our wrapping offset. * (If we've never before wrapped or we're not about to, no adjustment * is required.) */ if ((buf->dtb_flags & DTRACEBUF_WRAPPED) || offs + total > buf->dtb_size) { woffs = buf->dtb_xamot_offset; if (offs + total > buf->dtb_size) { /* * We can't fit in the end of the buffer. First, a * sanity check that we can fit in the buffer at all. */ if (total > buf->dtb_size) { dtrace_buffer_drop(buf); return (-1); } /* * We're going to be storing at the top of the buffer, * so now we need to deal with the wrapped offset. We * only reset our wrapped offset to 0 if it is * currently greater than the current offset. If it * is less than the current offset, it is because a * previous allocation induced a wrap -- but the * allocation didn't subsequently take the space due * to an error or false predicate evaluation. In this * case, we'll just leave the wrapped offset alone: if * the wrapped offset hasn't been advanced far enough * for this allocation, it will be adjusted in the * lower loop. */ if (buf->dtb_flags & DTRACEBUF_WRAPPED) { if (woffs >= offs) woffs = 0; } else { woffs = 0; } /* * Now we know that we're going to be storing to the * top of the buffer and that there is room for us * there. We need to clear the buffer from the current * offset to the end (there may be old gunk there). */ while (offs < buf->dtb_size) tomax[offs++] = 0; /* * We need to set our offset to zero. And because we * are wrapping, we need to set the bit indicating as * much. We can also adjust our needed space back * down to the space required by the ECB -- we know * that the top of the buffer is aligned. */ offs = 0; total = needed; buf->dtb_flags |= DTRACEBUF_WRAPPED; } else { /* * There is room for us in the buffer, so we simply * need to check the wrapped offset. */ if (woffs < offs) { /* * The wrapped offset is less than the offset. * This can happen if we allocated buffer space * that induced a wrap, but then we didn't * subsequently take the space due to an error * or false predicate evaluation. This is * okay; we know that _this_ allocation isn't * going to induce a wrap. We still can't * reset the wrapped offset to be zero, * however: the space may have been trashed in * the previous failed probe attempt. But at * least the wrapped offset doesn't need to * be adjusted at all... */ goto out; } } while (offs + total > woffs) { dtrace_epid_t epid = *(uint32_t *)(tomax + woffs); size_t size; if (epid == DTRACE_EPIDNONE) { size = sizeof (uint32_t); } else { ASSERT3U(epid, <=, state->dts_necbs); ASSERT(state->dts_ecbs[epid - 1] != NULL); size = state->dts_ecbs[epid - 1]->dte_size; } ASSERT(woffs + size <= buf->dtb_size); ASSERT(size != 0); if (woffs + size == buf->dtb_size) { /* * We've reached the end of the buffer; we want * to set the wrapped offset to 0 and break * out. However, if the offs is 0, then we're * in a strange edge-condition: the amount of * space that we want to reserve plus the size * of the record that we're overwriting is * greater than the size of the buffer. This * is problematic because if we reserve the * space but subsequently don't consume it (due * to a failed predicate or error) the wrapped * offset will be 0 -- yet the EPID at offset 0 * will not be committed. This situation is * relatively easy to deal with: if we're in * this case, the buffer is indistinguishable * from one that hasn't wrapped; we need only * finish the job by clearing the wrapped bit, * explicitly setting the offset to be 0, and * zero'ing out the old data in the buffer. */ if (offs == 0) { buf->dtb_flags &= ~DTRACEBUF_WRAPPED; buf->dtb_offset = 0; woffs = total; while (woffs < buf->dtb_size) tomax[woffs++] = 0; } woffs = 0; break; } woffs += size; } /* * We have a wrapped offset. It may be that the wrapped offset * has become zero -- that's okay. */ buf->dtb_xamot_offset = woffs; } out: /* * Now we can plow the buffer with any necessary padding. */ while (offs & (align - 1)) { /* * Assert that our alignment is off by a number which * is itself sizeof (uint32_t) aligned. */ ASSERT(!((align - (offs & (align - 1))) & (sizeof (uint32_t) - 1))); DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE); offs += sizeof (uint32_t); } if (buf->dtb_flags & DTRACEBUF_FILL) { if (offs + needed > buf->dtb_size - state->dts_reserve) { buf->dtb_flags |= DTRACEBUF_FULL; return (-1); } } if (mstate == NULL) return (offs); /* * For ring buffers and fill buffers, the scratch space is always * the inactive buffer. */ mstate->dtms_scratch_base = (uintptr_t)buf->dtb_xamot; mstate->dtms_scratch_size = buf->dtb_size; mstate->dtms_scratch_ptr = mstate->dtms_scratch_base; return (offs); } static void dtrace_buffer_polish(dtrace_buffer_t *buf) { ASSERT(buf->dtb_flags & DTRACEBUF_RING); ASSERT(MUTEX_HELD(&dtrace_lock)); if (!(buf->dtb_flags & DTRACEBUF_WRAPPED)) return; /* * We need to polish the ring buffer. There are three cases: * * - The first (and presumably most common) is that there is no gap * between the buffer offset and the wrapped offset. In this case, * there is nothing in the buffer that isn't valid data; we can * mark the buffer as polished and return. * * - The second (less common than the first but still more common * than the third) is that there is a gap between the buffer offset * and the wrapped offset, and the wrapped offset is larger than the * buffer offset. This can happen because of an alignment issue, or * can happen because of a call to dtrace_buffer_reserve() that * didn't subsequently consume the buffer space. In this case, * we need to zero the data from the buffer offset to the wrapped * offset. * * - The third (and least common) is that there is a gap between the * buffer offset and the wrapped offset, but the wrapped offset is * _less_ than the buffer offset. This can only happen because a * call to dtrace_buffer_reserve() induced a wrap, but the space * was not subsequently consumed. In this case, we need to zero the * space from the offset to the end of the buffer _and_ from the * top of the buffer to the wrapped offset. */ if (buf->dtb_offset < buf->dtb_xamot_offset) { bzero(buf->dtb_tomax + buf->dtb_offset, buf->dtb_xamot_offset - buf->dtb_offset); } if (buf->dtb_offset > buf->dtb_xamot_offset) { bzero(buf->dtb_tomax + buf->dtb_offset, buf->dtb_size - buf->dtb_offset); bzero(buf->dtb_tomax, buf->dtb_xamot_offset); } } /* * This routine determines if data generated at the specified time has likely * been entirely consumed at user-level. This routine is called to determine * if an ECB on a defunct probe (but for an active enabling) can be safely * disabled and destroyed. */ static int dtrace_buffer_consumed(dtrace_buffer_t *bufs, hrtime_t when) { int i; for (i = 0; i < NCPU; i++) { dtrace_buffer_t *buf = &bufs[i]; if (buf->dtb_size == 0) continue; if (buf->dtb_flags & DTRACEBUF_RING) return (0); if (!buf->dtb_switched && buf->dtb_offset != 0) return (0); if (buf->dtb_switched - buf->dtb_interval < when) return (0); } return (1); } static void dtrace_buffer_free(dtrace_buffer_t *bufs) { int i; for (i = 0; i < NCPU; i++) { dtrace_buffer_t *buf = &bufs[i]; if (buf->dtb_tomax == NULL) { ASSERT(buf->dtb_xamot == NULL); ASSERT(buf->dtb_size == 0); continue; } if (buf->dtb_xamot != NULL) { ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); kmem_free(buf->dtb_xamot, buf->dtb_size); } kmem_free(buf->dtb_tomax, buf->dtb_size); buf->dtb_size = 0; buf->dtb_tomax = NULL; buf->dtb_xamot = NULL; } } /* * DTrace Enabling Functions */ static dtrace_enabling_t * dtrace_enabling_create(dtrace_vstate_t *vstate) { dtrace_enabling_t *enab; enab = kmem_zalloc(sizeof (dtrace_enabling_t), KM_SLEEP); enab->dten_vstate = vstate; return (enab); } static void dtrace_enabling_add(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb) { dtrace_ecbdesc_t **ndesc; size_t osize, nsize; /* * We can't add to enablings after we've enabled them, or after we've * retained them. */ ASSERT(enab->dten_probegen == 0); ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL); if (enab->dten_ndesc < enab->dten_maxdesc) { enab->dten_desc[enab->dten_ndesc++] = ecb; return; } osize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *); if (enab->dten_maxdesc == 0) { enab->dten_maxdesc = 1; } else { enab->dten_maxdesc <<= 1; } ASSERT(enab->dten_ndesc < enab->dten_maxdesc); nsize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *); ndesc = kmem_zalloc(nsize, KM_SLEEP); bcopy(enab->dten_desc, ndesc, osize); if (enab->dten_desc != NULL) kmem_free(enab->dten_desc, osize); enab->dten_desc = ndesc; enab->dten_desc[enab->dten_ndesc++] = ecb; } static void dtrace_enabling_addlike(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb, dtrace_probedesc_t *pd) { dtrace_ecbdesc_t *new; dtrace_predicate_t *pred; dtrace_actdesc_t *act; /* * We're going to create a new ECB description that matches the * specified ECB in every way, but has the specified probe description. */ new = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP); if ((pred = ecb->dted_pred.dtpdd_predicate) != NULL) dtrace_predicate_hold(pred); for (act = ecb->dted_action; act != NULL; act = act->dtad_next) dtrace_actdesc_hold(act); new->dted_action = ecb->dted_action; new->dted_pred = ecb->dted_pred; new->dted_probe = *pd; new->dted_uarg = ecb->dted_uarg; dtrace_enabling_add(enab, new); } static void dtrace_enabling_dump(dtrace_enabling_t *enab) { int i; for (i = 0; i < enab->dten_ndesc; i++) { dtrace_probedesc_t *desc = &enab->dten_desc[i]->dted_probe; cmn_err(CE_NOTE, "enabling probe %d (%s:%s:%s:%s)", i, desc->dtpd_provider, desc->dtpd_mod, desc->dtpd_func, desc->dtpd_name); } } static void dtrace_enabling_destroy(dtrace_enabling_t *enab) { int i; dtrace_ecbdesc_t *ep; dtrace_vstate_t *vstate = enab->dten_vstate; ASSERT(MUTEX_HELD(&dtrace_lock)); for (i = 0; i < enab->dten_ndesc; i++) { dtrace_actdesc_t *act, *next; dtrace_predicate_t *pred; ep = enab->dten_desc[i]; if ((pred = ep->dted_pred.dtpdd_predicate) != NULL) dtrace_predicate_release(pred, vstate); for (act = ep->dted_action; act != NULL; act = next) { next = act->dtad_next; dtrace_actdesc_release(act, vstate); } kmem_free(ep, sizeof (dtrace_ecbdesc_t)); } if (enab->dten_desc != NULL) kmem_free(enab->dten_desc, enab->dten_maxdesc * sizeof (dtrace_enabling_t *)); /* * If this was a retained enabling, decrement the dts_nretained count * and take it off of the dtrace_retained list. */ if (enab->dten_prev != NULL || enab->dten_next != NULL || dtrace_retained == enab) { ASSERT(enab->dten_vstate->dtvs_state != NULL); ASSERT(enab->dten_vstate->dtvs_state->dts_nretained > 0); enab->dten_vstate->dtvs_state->dts_nretained--; dtrace_retained_gen++; } if (enab->dten_prev == NULL) { if (dtrace_retained == enab) { dtrace_retained = enab->dten_next; if (dtrace_retained != NULL) dtrace_retained->dten_prev = NULL; } } else { ASSERT(enab != dtrace_retained); ASSERT(dtrace_retained != NULL); enab->dten_prev->dten_next = enab->dten_next; } if (enab->dten_next != NULL) { ASSERT(dtrace_retained != NULL); enab->dten_next->dten_prev = enab->dten_prev; } kmem_free(enab, sizeof (dtrace_enabling_t)); } static int dtrace_enabling_retain(dtrace_enabling_t *enab) { dtrace_state_t *state; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL); ASSERT(enab->dten_vstate != NULL); state = enab->dten_vstate->dtvs_state; ASSERT(state != NULL); /* * We only allow each state to retain dtrace_retain_max enablings. */ if (state->dts_nretained >= dtrace_retain_max) return (ENOSPC); state->dts_nretained++; dtrace_retained_gen++; if (dtrace_retained == NULL) { dtrace_retained = enab; return (0); } enab->dten_next = dtrace_retained; dtrace_retained->dten_prev = enab; dtrace_retained = enab; return (0); } static int dtrace_enabling_replicate(dtrace_state_t *state, dtrace_probedesc_t *match, dtrace_probedesc_t *create) { dtrace_enabling_t *new, *enab; int found = 0, err = ENOENT; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(strlen(match->dtpd_provider) < DTRACE_PROVNAMELEN); ASSERT(strlen(match->dtpd_mod) < DTRACE_MODNAMELEN); ASSERT(strlen(match->dtpd_func) < DTRACE_FUNCNAMELEN); ASSERT(strlen(match->dtpd_name) < DTRACE_NAMELEN); new = dtrace_enabling_create(&state->dts_vstate); /* * Iterate over all retained enablings, looking for enablings that * match the specified state. */ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { int i; /* * dtvs_state can only be NULL for helper enablings -- and * helper enablings can't be retained. */ ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state != state) continue; /* * Now iterate over each probe description; we're looking for * an exact match to the specified probe description. */ for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; dtrace_probedesc_t *pd = &ep->dted_probe; if (strcmp(pd->dtpd_provider, match->dtpd_provider)) continue; if (strcmp(pd->dtpd_mod, match->dtpd_mod)) continue; if (strcmp(pd->dtpd_func, match->dtpd_func)) continue; if (strcmp(pd->dtpd_name, match->dtpd_name)) continue; /* * We have a winning probe! Add it to our growing * enabling. */ found = 1; dtrace_enabling_addlike(new, ep, create); } } if (!found || (err = dtrace_enabling_retain(new)) != 0) { dtrace_enabling_destroy(new); return (err); } return (0); } static void dtrace_enabling_retract(dtrace_state_t *state) { dtrace_enabling_t *enab, *next; ASSERT(MUTEX_HELD(&dtrace_lock)); /* * Iterate over all retained enablings, destroy the enablings retained * for the specified state. */ for (enab = dtrace_retained; enab != NULL; enab = next) { next = enab->dten_next; /* * dtvs_state can only be NULL for helper enablings -- and * helper enablings can't be retained. */ ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state == state) { ASSERT(state->dts_nretained > 0); dtrace_enabling_destroy(enab); } } ASSERT(state->dts_nretained == 0); } static int dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched) { int i = 0; int matched = 0; ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; enab->dten_current = ep; enab->dten_error = 0; matched += dtrace_probe_enable(&ep->dted_probe, enab); if (enab->dten_error != 0) { /* * If we get an error half-way through enabling the * probes, we kick out -- perhaps with some number of * them enabled. Leaving enabled probes enabled may * be slightly confusing for user-level, but we expect * that no one will attempt to actually drive on in * the face of such errors. If this is an anonymous * enabling (indicated with a NULL nmatched pointer), * we cmn_err() a message. We aren't expecting to * get such an error -- such as it can exist at all, * it would be a result of corrupted DOF in the driver * properties. */ if (nmatched == NULL) { cmn_err(CE_WARN, "dtrace_enabling_match() " "error on %p: %d", (void *)ep, enab->dten_error); } return (enab->dten_error); } } enab->dten_probegen = dtrace_probegen; if (nmatched != NULL) *nmatched = matched; return (0); } static void dtrace_enabling_matchall(void) { dtrace_enabling_t *enab; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); /* * Iterate over all retained enablings to see if any probes match * against them. We only perform this operation on enablings for which * we have sufficient permissions by virtue of being in the global zone * or in the same zone as the DTrace client. Because we can be called * after dtrace_detach() has been called, we cannot assert that there * are retained enablings. We can safely load from dtrace_retained, * however: the taskq_destroy() at the end of dtrace_detach() will * block pending our completion. */ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { #ifdef illumos cred_t *cr = enab->dten_vstate->dtvs_state->dts_cred.dcr_cred; if (INGLOBALZONE(curproc) || cr != NULL && getzoneid() == crgetzoneid(cr)) #endif (void) dtrace_enabling_match(enab, NULL); } mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); } /* * If an enabling is to be enabled without having matched probes (that is, if * dtrace_state_go() is to be called on the underlying dtrace_state_t), the * enabling must be _primed_ by creating an ECB for every ECB description. * This must be done to assure that we know the number of speculations, the * number of aggregations, the minimum buffer size needed, etc. before we * transition out of DTRACE_ACTIVITY_INACTIVE. To do this without actually * enabling any probes, we create ECBs for every ECB decription, but with a * NULL probe -- which is exactly what this function does. */ static void dtrace_enabling_prime(dtrace_state_t *state) { dtrace_enabling_t *enab; int i; for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { ASSERT(enab->dten_vstate->dtvs_state != NULL); if (enab->dten_vstate->dtvs_state != state) continue; /* * We don't want to prime an enabling more than once, lest * we allow a malicious user to induce resource exhaustion. * (The ECBs that result from priming an enabling aren't * leaked -- but they also aren't deallocated until the * consumer state is destroyed.) */ if (enab->dten_primed) continue; for (i = 0; i < enab->dten_ndesc; i++) { enab->dten_current = enab->dten_desc[i]; (void) dtrace_probe_enable(NULL, enab); } enab->dten_primed = 1; } } /* * Called to indicate that probes should be provided due to retained * enablings. This is implemented in terms of dtrace_probe_provide(), but it * must take an initial lap through the enabling calling the dtps_provide() * entry point explicitly to allow for autocreated probes. */ static void dtrace_enabling_provide(dtrace_provider_t *prv) { int i, all = 0; dtrace_probedesc_t desc; dtrace_genid_t gen; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&dtrace_provider_lock)); if (prv == NULL) { all = 1; prv = dtrace_provider; } do { dtrace_enabling_t *enab; void *parg = prv->dtpv_arg; retry: gen = dtrace_retained_gen; for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) { for (i = 0; i < enab->dten_ndesc; i++) { desc = enab->dten_desc[i]->dted_probe; mutex_exit(&dtrace_lock); prv->dtpv_pops.dtps_provide(parg, &desc); mutex_enter(&dtrace_lock); /* * Process the retained enablings again if * they have changed while we weren't holding * dtrace_lock. */ if (gen != dtrace_retained_gen) goto retry; } } } while (all && (prv = prv->dtpv_next) != NULL); mutex_exit(&dtrace_lock); dtrace_probe_provide(NULL, all ? NULL : prv); mutex_enter(&dtrace_lock); } /* * Called to reap ECBs that are attached to probes from defunct providers. */ static void dtrace_enabling_reap(void) { dtrace_provider_t *prov; dtrace_probe_t *probe; dtrace_ecb_t *ecb; hrtime_t when; int i; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); for (i = 0; i < dtrace_nprobes; i++) { if ((probe = dtrace_probes[i]) == NULL) continue; if (probe->dtpr_ecb == NULL) continue; prov = probe->dtpr_provider; if ((when = prov->dtpv_defunct) == 0) continue; /* * We have ECBs on a defunct provider: we want to reap these * ECBs to allow the provider to unregister. The destruction * of these ECBs must be done carefully: if we destroy the ECB * and the consumer later wishes to consume an EPID that * corresponds to the destroyed ECB (and if the EPID metadata * has not been previously consumed), the consumer will abort * processing on the unknown EPID. To reduce (but not, sadly, * eliminate) the possibility of this, we will only destroy an * ECB for a defunct provider if, for the state that * corresponds to the ECB: * * (a) There is no speculative tracing (which can effectively * cache an EPID for an arbitrary amount of time). * * (b) The principal buffers have been switched twice since the * provider became defunct. * * (c) The aggregation buffers are of zero size or have been * switched twice since the provider became defunct. * * We use dts_speculates to determine (a) and call a function * (dtrace_buffer_consumed()) to determine (b) and (c). Note * that as soon as we've been unable to destroy one of the ECBs * associated with the probe, we quit trying -- reaping is only * fruitful in as much as we can destroy all ECBs associated * with the defunct provider's probes. */ while ((ecb = probe->dtpr_ecb) != NULL) { dtrace_state_t *state = ecb->dte_state; dtrace_buffer_t *buf = state->dts_buffer; dtrace_buffer_t *aggbuf = state->dts_aggbuffer; if (state->dts_speculates) break; if (!dtrace_buffer_consumed(buf, when)) break; if (!dtrace_buffer_consumed(aggbuf, when)) break; dtrace_ecb_disable(ecb); ASSERT(probe->dtpr_ecb != ecb); dtrace_ecb_destroy(ecb); } } mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); } /* * DTrace DOF Functions */ /*ARGSUSED*/ static void dtrace_dof_error(dof_hdr_t *dof, const char *str) { if (dtrace_err_verbose) cmn_err(CE_WARN, "failed to process DOF: %s", str); #ifdef DTRACE_ERRDEBUG dtrace_errdebug(str); #endif } /* * Create DOF out of a currently enabled state. Right now, we only create * DOF containing the run-time options -- but this could be expanded to create * complete DOF representing the enabled state. */ static dof_hdr_t * dtrace_dof_create(dtrace_state_t *state) { dof_hdr_t *dof; dof_sec_t *sec; dof_optdesc_t *opt; int i, len = sizeof (dof_hdr_t) + roundup(sizeof (dof_sec_t), sizeof (uint64_t)) + sizeof (dof_optdesc_t) * DTRACEOPT_MAX; ASSERT(MUTEX_HELD(&dtrace_lock)); dof = kmem_zalloc(len, KM_SLEEP); dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE; dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION; dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION; dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS; dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS; dof->dofh_flags = 0; dof->dofh_hdrsize = sizeof (dof_hdr_t); dof->dofh_secsize = sizeof (dof_sec_t); dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */ dof->dofh_secoff = sizeof (dof_hdr_t); dof->dofh_loadsz = len; dof->dofh_filesz = len; dof->dofh_pad = 0; /* * Fill in the option section header... */ sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t)); sec->dofs_type = DOF_SECT_OPTDESC; sec->dofs_align = sizeof (uint64_t); sec->dofs_flags = DOF_SECF_LOAD; sec->dofs_entsize = sizeof (dof_optdesc_t); opt = (dof_optdesc_t *)((uintptr_t)sec + roundup(sizeof (dof_sec_t), sizeof (uint64_t))); sec->dofs_offset = (uintptr_t)opt - (uintptr_t)dof; sec->dofs_size = sizeof (dof_optdesc_t) * DTRACEOPT_MAX; for (i = 0; i < DTRACEOPT_MAX; i++) { opt[i].dofo_option = i; opt[i].dofo_strtab = DOF_SECIDX_NONE; opt[i].dofo_value = state->dts_options[i]; } return (dof); } static dof_hdr_t * dtrace_dof_copyin(uintptr_t uarg, int *errp) { dof_hdr_t hdr, *dof; ASSERT(!MUTEX_HELD(&dtrace_lock)); /* * First, we're going to copyin() the sizeof (dof_hdr_t). */ if (copyin((void *)uarg, &hdr, sizeof (hdr)) != 0) { dtrace_dof_error(NULL, "failed to copyin DOF header"); *errp = EFAULT; return (NULL); } /* * Now we'll allocate the entire DOF and copy it in -- provided * that the length isn't outrageous. */ if (hdr.dofh_loadsz >= dtrace_dof_maxsize) { dtrace_dof_error(&hdr, "load size exceeds maximum"); *errp = E2BIG; return (NULL); } if (hdr.dofh_loadsz < sizeof (hdr)) { dtrace_dof_error(&hdr, "invalid load size"); *errp = EINVAL; return (NULL); } dof = kmem_alloc(hdr.dofh_loadsz, KM_SLEEP); if (copyin((void *)uarg, dof, hdr.dofh_loadsz) != 0 || dof->dofh_loadsz != hdr.dofh_loadsz) { kmem_free(dof, hdr.dofh_loadsz); *errp = EFAULT; return (NULL); } return (dof); } #ifndef illumos static __inline uchar_t dtrace_dof_char(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return (c - '0'); case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return (c - 'A' + 10); case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return (c - 'a' + 10); } /* Should not reach here. */ return (0); } #endif static dof_hdr_t * dtrace_dof_property(const char *name) { uchar_t *buf; uint64_t loadsz; unsigned int len, i; dof_hdr_t *dof; #ifdef illumos /* * Unfortunately, array of values in .conf files are always (and * only) interpreted to be integer arrays. We must read our DOF * as an integer array, and then squeeze it into a byte array. */ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dtrace_devi, 0, (char *)name, (int **)&buf, &len) != DDI_PROP_SUCCESS) return (NULL); for (i = 0; i < len; i++) buf[i] = (uchar_t)(((int *)buf)[i]); if (len < sizeof (dof_hdr_t)) { ddi_prop_free(buf); dtrace_dof_error(NULL, "truncated header"); return (NULL); } if (len < (loadsz = ((dof_hdr_t *)buf)->dofh_loadsz)) { ddi_prop_free(buf); dtrace_dof_error(NULL, "truncated DOF"); return (NULL); } if (loadsz >= dtrace_dof_maxsize) { ddi_prop_free(buf); dtrace_dof_error(NULL, "oversized DOF"); return (NULL); } dof = kmem_alloc(loadsz, KM_SLEEP); bcopy(buf, dof, loadsz); ddi_prop_free(buf); #else char *p; char *p_env; if ((p_env = kern_getenv(name)) == NULL) return (NULL); len = strlen(p_env) / 2; buf = kmem_alloc(len, KM_SLEEP); dof = (dof_hdr_t *) buf; p = p_env; for (i = 0; i < len; i++) { buf[i] = (dtrace_dof_char(p[0]) << 4) | dtrace_dof_char(p[1]); p += 2; } freeenv(p_env); if (len < sizeof (dof_hdr_t)) { kmem_free(buf, 0); dtrace_dof_error(NULL, "truncated header"); return (NULL); } if (len < (loadsz = dof->dofh_loadsz)) { kmem_free(buf, 0); dtrace_dof_error(NULL, "truncated DOF"); return (NULL); } if (loadsz >= dtrace_dof_maxsize) { kmem_free(buf, 0); dtrace_dof_error(NULL, "oversized DOF"); return (NULL); } #endif return (dof); } static void dtrace_dof_destroy(dof_hdr_t *dof) { kmem_free(dof, dof->dofh_loadsz); } /* * Return the dof_sec_t pointer corresponding to a given section index. If the * index is not valid, dtrace_dof_error() is called and NULL is returned. If * a type other than DOF_SECT_NONE is specified, the header is checked against * this type and NULL is returned if the types do not match. */ static dof_sec_t * dtrace_dof_sect(dof_hdr_t *dof, uint32_t type, dof_secidx_t i) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t) ((uintptr_t)dof + dof->dofh_secoff + i * dof->dofh_secsize); if (i >= dof->dofh_secnum) { dtrace_dof_error(dof, "referenced section index is invalid"); return (NULL); } if (!(sec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "referenced section is not loadable"); return (NULL); } if (type != DOF_SECT_NONE && type != sec->dofs_type) { dtrace_dof_error(dof, "referenced section is the wrong type"); return (NULL); } return (sec); } static dtrace_probedesc_t * dtrace_dof_probedesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_probedesc_t *desc) { dof_probedesc_t *probe; dof_sec_t *strtab; uintptr_t daddr = (uintptr_t)dof; uintptr_t str; size_t size; if (sec->dofs_type != DOF_SECT_PROBEDESC) { dtrace_dof_error(dof, "invalid probe section"); return (NULL); } if (sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad alignment in probe description"); return (NULL); } if (sec->dofs_offset + sizeof (dof_probedesc_t) > dof->dofh_loadsz) { dtrace_dof_error(dof, "truncated probe description"); return (NULL); } probe = (dof_probedesc_t *)(uintptr_t)(daddr + sec->dofs_offset); strtab = dtrace_dof_sect(dof, DOF_SECT_STRTAB, probe->dofp_strtab); if (strtab == NULL) return (NULL); str = daddr + strtab->dofs_offset; size = strtab->dofs_size; if (probe->dofp_provider >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe provider"); return (NULL); } (void) strncpy(desc->dtpd_provider, (char *)(str + probe->dofp_provider), MIN(DTRACE_PROVNAMELEN - 1, size - probe->dofp_provider)); if (probe->dofp_mod >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe module"); return (NULL); } (void) strncpy(desc->dtpd_mod, (char *)(str + probe->dofp_mod), MIN(DTRACE_MODNAMELEN - 1, size - probe->dofp_mod)); if (probe->dofp_func >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe function"); return (NULL); } (void) strncpy(desc->dtpd_func, (char *)(str + probe->dofp_func), MIN(DTRACE_FUNCNAMELEN - 1, size - probe->dofp_func)); if (probe->dofp_name >= strtab->dofs_size) { dtrace_dof_error(dof, "corrupt probe name"); return (NULL); } (void) strncpy(desc->dtpd_name, (char *)(str + probe->dofp_name), MIN(DTRACE_NAMELEN - 1, size - probe->dofp_name)); return (desc); } static dtrace_difo_t * dtrace_dof_difo(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_difo_t *dp; size_t ttl = 0; dof_difohdr_t *dofd; uintptr_t daddr = (uintptr_t)dof; size_t max = dtrace_difo_maxsize; int i, l, n; static const struct { int section; int bufoffs; int lenoffs; int entsize; int align; const char *msg; } difo[] = { { DOF_SECT_DIF, offsetof(dtrace_difo_t, dtdo_buf), offsetof(dtrace_difo_t, dtdo_len), sizeof (dif_instr_t), sizeof (dif_instr_t), "multiple DIF sections" }, { DOF_SECT_INTTAB, offsetof(dtrace_difo_t, dtdo_inttab), offsetof(dtrace_difo_t, dtdo_intlen), sizeof (uint64_t), sizeof (uint64_t), "multiple integer tables" }, { DOF_SECT_STRTAB, offsetof(dtrace_difo_t, dtdo_strtab), offsetof(dtrace_difo_t, dtdo_strlen), 0, sizeof (char), "multiple string tables" }, { DOF_SECT_VARTAB, offsetof(dtrace_difo_t, dtdo_vartab), offsetof(dtrace_difo_t, dtdo_varlen), sizeof (dtrace_difv_t), sizeof (uint_t), "multiple variable tables" }, { DOF_SECT_NONE, 0, 0, 0, 0, NULL } }; if (sec->dofs_type != DOF_SECT_DIFOHDR) { dtrace_dof_error(dof, "invalid DIFO header section"); return (NULL); } if (sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad alignment in DIFO header"); return (NULL); } if (sec->dofs_size < sizeof (dof_difohdr_t) || sec->dofs_size % sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "bad size in DIFO header"); return (NULL); } dofd = (dof_difohdr_t *)(uintptr_t)(daddr + sec->dofs_offset); n = (sec->dofs_size - sizeof (*dofd)) / sizeof (dof_secidx_t) + 1; dp = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP); dp->dtdo_rtype = dofd->dofd_rtype; for (l = 0; l < n; l++) { dof_sec_t *subsec; void **bufp; uint32_t *lenp; if ((subsec = dtrace_dof_sect(dof, DOF_SECT_NONE, dofd->dofd_links[l])) == NULL) goto err; /* invalid section link */ if (ttl + subsec->dofs_size > max) { dtrace_dof_error(dof, "exceeds maximum size"); goto err; } ttl += subsec->dofs_size; for (i = 0; difo[i].section != DOF_SECT_NONE; i++) { if (subsec->dofs_type != difo[i].section) continue; if (!(subsec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "section not loaded"); goto err; } if (subsec->dofs_align != difo[i].align) { dtrace_dof_error(dof, "bad alignment"); goto err; } bufp = (void **)((uintptr_t)dp + difo[i].bufoffs); lenp = (uint32_t *)((uintptr_t)dp + difo[i].lenoffs); if (*bufp != NULL) { dtrace_dof_error(dof, difo[i].msg); goto err; } if (difo[i].entsize != subsec->dofs_entsize) { dtrace_dof_error(dof, "entry size mismatch"); goto err; } if (subsec->dofs_entsize != 0 && (subsec->dofs_size % subsec->dofs_entsize) != 0) { dtrace_dof_error(dof, "corrupt entry size"); goto err; } *lenp = subsec->dofs_size; *bufp = kmem_alloc(subsec->dofs_size, KM_SLEEP); bcopy((char *)(uintptr_t)(daddr + subsec->dofs_offset), *bufp, subsec->dofs_size); if (subsec->dofs_entsize != 0) *lenp /= subsec->dofs_entsize; break; } /* * If we encounter a loadable DIFO sub-section that is not * known to us, assume this is a broken program and fail. */ if (difo[i].section == DOF_SECT_NONE && (subsec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "unrecognized DIFO subsection"); goto err; } } if (dp->dtdo_buf == NULL) { /* * We can't have a DIF object without DIF text. */ dtrace_dof_error(dof, "missing DIF text"); goto err; } /* * Before we validate the DIF object, run through the variable table * looking for the strings -- if any of their size are under, we'll set * their size to be the system-wide default string size. Note that * this should _not_ happen if the "strsize" option has been set -- * in this case, the compiler should have set the size to reflect the * setting of the option. */ for (i = 0; i < dp->dtdo_varlen; i++) { dtrace_difv_t *v = &dp->dtdo_vartab[i]; dtrace_diftype_t *t = &v->dtdv_type; if (v->dtdv_id < DIF_VAR_OTHER_UBASE) continue; if (t->dtdt_kind == DIF_TYPE_STRING && t->dtdt_size == 0) t->dtdt_size = dtrace_strsize_default; } if (dtrace_difo_validate(dp, vstate, DIF_DIR_NREGS, cr) != 0) goto err; dtrace_difo_init(dp, vstate); return (dp); err: kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t)); kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t)); kmem_free(dp->dtdo_strtab, dp->dtdo_strlen); kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t)); kmem_free(dp, sizeof (dtrace_difo_t)); return (NULL); } static dtrace_predicate_t * dtrace_dof_predicate(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_difo_t *dp; if ((dp = dtrace_dof_difo(dof, sec, vstate, cr)) == NULL) return (NULL); return (dtrace_predicate_create(dp)); } static dtrace_actdesc_t * dtrace_dof_actdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_actdesc_t *act, *first = NULL, *last = NULL, *next; dof_actdesc_t *desc; dof_sec_t *difosec; size_t offs; uintptr_t daddr = (uintptr_t)dof; uint64_t arg; dtrace_actkind_t kind; if (sec->dofs_type != DOF_SECT_ACTDESC) { dtrace_dof_error(dof, "invalid action section"); return (NULL); } if (sec->dofs_offset + sizeof (dof_actdesc_t) > dof->dofh_loadsz) { dtrace_dof_error(dof, "truncated action description"); return (NULL); } if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in action description"); return (NULL); } if (sec->dofs_size < sec->dofs_entsize) { dtrace_dof_error(dof, "section entry size exceeds total size"); return (NULL); } if (sec->dofs_entsize != sizeof (dof_actdesc_t)) { dtrace_dof_error(dof, "bad entry size in action description"); return (NULL); } if (sec->dofs_size / sec->dofs_entsize > dtrace_actions_max) { dtrace_dof_error(dof, "actions exceed dtrace_actions_max"); return (NULL); } for (offs = 0; offs < sec->dofs_size; offs += sec->dofs_entsize) { desc = (dof_actdesc_t *)(daddr + (uintptr_t)sec->dofs_offset + offs); kind = (dtrace_actkind_t)desc->dofa_kind; if ((DTRACEACT_ISPRINTFLIKE(kind) && (kind != DTRACEACT_PRINTA || desc->dofa_strtab != DOF_SECIDX_NONE)) || (kind == DTRACEACT_DIFEXPR && desc->dofa_strtab != DOF_SECIDX_NONE)) { dof_sec_t *strtab; char *str, *fmt; uint64_t i; /* * The argument to these actions is an index into the * DOF string table. For printf()-like actions, this * is the format string. For print(), this is the * CTF type of the expression result. */ if ((strtab = dtrace_dof_sect(dof, DOF_SECT_STRTAB, desc->dofa_strtab)) == NULL) goto err; str = (char *)((uintptr_t)dof + (uintptr_t)strtab->dofs_offset); for (i = desc->dofa_arg; i < strtab->dofs_size; i++) { if (str[i] == '\0') break; } if (i >= strtab->dofs_size) { dtrace_dof_error(dof, "bogus format string"); goto err; } if (i == desc->dofa_arg) { dtrace_dof_error(dof, "empty format string"); goto err; } i -= desc->dofa_arg; fmt = kmem_alloc(i + 1, KM_SLEEP); bcopy(&str[desc->dofa_arg], fmt, i + 1); arg = (uint64_t)(uintptr_t)fmt; } else { if (kind == DTRACEACT_PRINTA) { ASSERT(desc->dofa_strtab == DOF_SECIDX_NONE); arg = 0; } else { arg = desc->dofa_arg; } } act = dtrace_actdesc_create(kind, desc->dofa_ntuple, desc->dofa_uarg, arg); if (last != NULL) { last->dtad_next = act; } else { first = act; } last = act; if (desc->dofa_difo == DOF_SECIDX_NONE) continue; if ((difosec = dtrace_dof_sect(dof, DOF_SECT_DIFOHDR, desc->dofa_difo)) == NULL) goto err; act->dtad_difo = dtrace_dof_difo(dof, difosec, vstate, cr); if (act->dtad_difo == NULL) goto err; } ASSERT(first != NULL); return (first); err: for (act = first; act != NULL; act = next) { next = act->dtad_next; dtrace_actdesc_release(act, vstate); } return (NULL); } static dtrace_ecbdesc_t * dtrace_dof_ecbdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate, cred_t *cr) { dtrace_ecbdesc_t *ep; dof_ecbdesc_t *ecb; dtrace_probedesc_t *desc; dtrace_predicate_t *pred = NULL; if (sec->dofs_size < sizeof (dof_ecbdesc_t)) { dtrace_dof_error(dof, "truncated ECB description"); return (NULL); } if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in ECB description"); return (NULL); } ecb = (dof_ecbdesc_t *)((uintptr_t)dof + (uintptr_t)sec->dofs_offset); sec = dtrace_dof_sect(dof, DOF_SECT_PROBEDESC, ecb->dofe_probes); if (sec == NULL) return (NULL); ep = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP); ep->dted_uarg = ecb->dofe_uarg; desc = &ep->dted_probe; if (dtrace_dof_probedesc(dof, sec, desc) == NULL) goto err; if (ecb->dofe_pred != DOF_SECIDX_NONE) { if ((sec = dtrace_dof_sect(dof, DOF_SECT_DIFOHDR, ecb->dofe_pred)) == NULL) goto err; if ((pred = dtrace_dof_predicate(dof, sec, vstate, cr)) == NULL) goto err; ep->dted_pred.dtpdd_predicate = pred; } if (ecb->dofe_actions != DOF_SECIDX_NONE) { if ((sec = dtrace_dof_sect(dof, DOF_SECT_ACTDESC, ecb->dofe_actions)) == NULL) goto err; ep->dted_action = dtrace_dof_actdesc(dof, sec, vstate, cr); if (ep->dted_action == NULL) goto err; } return (ep); err: if (pred != NULL) dtrace_predicate_release(pred, vstate); kmem_free(ep, sizeof (dtrace_ecbdesc_t)); return (NULL); } /* * Apply the relocations from the specified 'sec' (a DOF_SECT_URELHDR) to the * specified DOF. At present, this amounts to simply adding 'ubase' to the * site of any user SETX relocations to account for load object base address. * In the future, if we need other relocations, this function can be extended. */ static int dtrace_dof_relocate(dof_hdr_t *dof, dof_sec_t *sec, uint64_t ubase) { uintptr_t daddr = (uintptr_t)dof; dof_relohdr_t *dofr = (dof_relohdr_t *)(uintptr_t)(daddr + sec->dofs_offset); dof_sec_t *ss, *rs, *ts; dof_relodesc_t *r; uint_t i, n; if (sec->dofs_size < sizeof (dof_relohdr_t) || sec->dofs_align != sizeof (dof_secidx_t)) { dtrace_dof_error(dof, "invalid relocation header"); return (-1); } ss = dtrace_dof_sect(dof, DOF_SECT_STRTAB, dofr->dofr_strtab); rs = dtrace_dof_sect(dof, DOF_SECT_RELTAB, dofr->dofr_relsec); ts = dtrace_dof_sect(dof, DOF_SECT_NONE, dofr->dofr_tgtsec); if (ss == NULL || rs == NULL || ts == NULL) return (-1); /* dtrace_dof_error() has been called already */ if (rs->dofs_entsize < sizeof (dof_relodesc_t) || rs->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "invalid relocation section"); return (-1); } r = (dof_relodesc_t *)(uintptr_t)(daddr + rs->dofs_offset); n = rs->dofs_size / rs->dofs_entsize; for (i = 0; i < n; i++) { uintptr_t taddr = daddr + ts->dofs_offset + r->dofr_offset; switch (r->dofr_type) { case DOF_RELO_NONE: break; case DOF_RELO_SETX: if (r->dofr_offset >= ts->dofs_size || r->dofr_offset + sizeof (uint64_t) > ts->dofs_size) { dtrace_dof_error(dof, "bad relocation offset"); return (-1); } if (!IS_P2ALIGNED(taddr, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned setx relo"); return (-1); } *(uint64_t *)taddr += ubase; break; default: dtrace_dof_error(dof, "invalid relocation type"); return (-1); } r = (dof_relodesc_t *)((uintptr_t)r + rs->dofs_entsize); } return (0); } /* * The dof_hdr_t passed to dtrace_dof_slurp() should be a partially validated * header: it should be at the front of a memory region that is at least * sizeof (dof_hdr_t) in size -- and then at least dof_hdr.dofh_loadsz in * size. It need not be validated in any other way. */ static int dtrace_dof_slurp(dof_hdr_t *dof, dtrace_vstate_t *vstate, cred_t *cr, dtrace_enabling_t **enabp, uint64_t ubase, int noprobes) { uint64_t len = dof->dofh_loadsz, seclen; uintptr_t daddr = (uintptr_t)dof; dtrace_ecbdesc_t *ep; dtrace_enabling_t *enab; uint_t i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dof->dofh_loadsz >= sizeof (dof_hdr_t)); /* * Check the DOF header identification bytes. In addition to checking * valid settings, we also verify that unused bits/bytes are zeroed so * we can use them later without fear of regressing existing binaries. */ if (bcmp(&dof->dofh_ident[DOF_ID_MAG0], DOF_MAG_STRING, DOF_MAG_STRLEN) != 0) { dtrace_dof_error(dof, "DOF magic string mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_ILP32 && dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_LP64) { dtrace_dof_error(dof, "DOF has invalid data model"); return (-1); } if (dof->dofh_ident[DOF_ID_ENCODING] != DOF_ENCODE_NATIVE) { dtrace_dof_error(dof, "DOF encoding mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_2) { dtrace_dof_error(dof, "DOF version mismatch"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFVERS] != DIF_VERSION_2) { dtrace_dof_error(dof, "DOF uses unsupported instruction set"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFIREG] > DIF_DIR_NREGS) { dtrace_dof_error(dof, "DOF uses too many integer registers"); return (-1); } if (dof->dofh_ident[DOF_ID_DIFTREG] > DIF_DTR_NREGS) { dtrace_dof_error(dof, "DOF uses too many tuple registers"); return (-1); } for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) { if (dof->dofh_ident[i] != 0) { dtrace_dof_error(dof, "DOF has invalid ident byte set"); return (-1); } } if (dof->dofh_flags & ~DOF_FL_VALID) { dtrace_dof_error(dof, "DOF has invalid flag bits set"); return (-1); } if (dof->dofh_secsize == 0) { dtrace_dof_error(dof, "zero section header size"); return (-1); } /* * Check that the section headers don't exceed the amount of DOF * data. Note that we cast the section size and number of sections * to uint64_t's to prevent possible overflow in the multiplication. */ seclen = (uint64_t)dof->dofh_secnum * (uint64_t)dof->dofh_secsize; if (dof->dofh_secoff > len || seclen > len || dof->dofh_secoff + seclen > len) { dtrace_dof_error(dof, "truncated section headers"); return (-1); } if (!IS_P2ALIGNED(dof->dofh_secoff, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned section headers"); return (-1); } if (!IS_P2ALIGNED(dof->dofh_secsize, sizeof (uint64_t))) { dtrace_dof_error(dof, "misaligned section size"); return (-1); } /* * Take an initial pass through the section headers to be sure that * the headers don't have stray offsets. If the 'noprobes' flag is * set, do not permit sections relating to providers, probes, or args. */ for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (noprobes) { switch (sec->dofs_type) { case DOF_SECT_PROVIDER: case DOF_SECT_PROBES: case DOF_SECT_PRARGS: case DOF_SECT_PROFFS: dtrace_dof_error(dof, "illegal sections " "for enabling"); return (-1); } } if (DOF_SEC_ISLOADABLE(sec->dofs_type) && !(sec->dofs_flags & DOF_SECF_LOAD)) { dtrace_dof_error(dof, "loadable section with load " "flag unset"); return (-1); } if (!(sec->dofs_flags & DOF_SECF_LOAD)) continue; /* just ignore non-loadable sections */ if (!ISP2(sec->dofs_align)) { dtrace_dof_error(dof, "bad section alignment"); return (-1); } if (sec->dofs_offset & (sec->dofs_align - 1)) { dtrace_dof_error(dof, "misaligned section"); return (-1); } if (sec->dofs_offset > len || sec->dofs_size > len || sec->dofs_offset + sec->dofs_size > len) { dtrace_dof_error(dof, "corrupt section header"); return (-1); } if (sec->dofs_type == DOF_SECT_STRTAB && *((char *)daddr + sec->dofs_offset + sec->dofs_size - 1) != '\0') { dtrace_dof_error(dof, "non-terminating string table"); return (-1); } } /* * Take a second pass through the sections and locate and perform any * relocations that are present. We do this after the first pass to * be sure that all sections have had their headers validated. */ for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (!(sec->dofs_flags & DOF_SECF_LOAD)) continue; /* skip sections that are not loadable */ switch (sec->dofs_type) { case DOF_SECT_URELHDR: if (dtrace_dof_relocate(dof, sec, ubase) != 0) return (-1); break; } } if ((enab = *enabp) == NULL) enab = *enabp = dtrace_enabling_create(vstate); for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_ECBDESC) continue; if ((ep = dtrace_dof_ecbdesc(dof, sec, vstate, cr)) == NULL) { dtrace_enabling_destroy(enab); *enabp = NULL; return (-1); } dtrace_enabling_add(enab, ep); } return (0); } /* * Process DOF for any options. This routine assumes that the DOF has been * at least processed by dtrace_dof_slurp(). */ static int dtrace_dof_options(dof_hdr_t *dof, dtrace_state_t *state) { int i, rval; uint32_t entsize; size_t offs; dof_optdesc_t *desc; for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)((uintptr_t)dof + (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_OPTDESC) continue; if (sec->dofs_align != sizeof (uint64_t)) { dtrace_dof_error(dof, "bad alignment in " "option description"); return (EINVAL); } if ((entsize = sec->dofs_entsize) == 0) { dtrace_dof_error(dof, "zeroed option entry size"); return (EINVAL); } if (entsize < sizeof (dof_optdesc_t)) { dtrace_dof_error(dof, "bad option entry size"); return (EINVAL); } for (offs = 0; offs < sec->dofs_size; offs += entsize) { desc = (dof_optdesc_t *)((uintptr_t)dof + (uintptr_t)sec->dofs_offset + offs); if (desc->dofo_strtab != DOF_SECIDX_NONE) { dtrace_dof_error(dof, "non-zero option string"); return (EINVAL); } if (desc->dofo_value == DTRACEOPT_UNSET) { dtrace_dof_error(dof, "unset option"); return (EINVAL); } if ((rval = dtrace_state_option(state, desc->dofo_option, desc->dofo_value)) != 0) { dtrace_dof_error(dof, "rejected option"); return (rval); } } } return (0); } /* * DTrace Consumer State Functions */ static int dtrace_dstate_init(dtrace_dstate_t *dstate, size_t size) { size_t hashsize, maxper, min, chunksize = dstate->dtds_chunksize; void *base; uintptr_t limit; dtrace_dynvar_t *dvar, *next, *start; int i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(dstate->dtds_base == NULL && dstate->dtds_percpu == NULL); bzero(dstate, sizeof (dtrace_dstate_t)); if ((dstate->dtds_chunksize = chunksize) == 0) dstate->dtds_chunksize = DTRACE_DYNVAR_CHUNKSIZE; if (size < (min = dstate->dtds_chunksize + sizeof (dtrace_dynhash_t))) size = min; if ((base = kmem_zalloc(size, KM_NOSLEEP | KM_NORMALPRI)) == NULL) return (ENOMEM); dstate->dtds_size = size; dstate->dtds_base = base; dstate->dtds_percpu = kmem_cache_alloc(dtrace_state_cache, KM_SLEEP); bzero(dstate->dtds_percpu, NCPU * sizeof (dtrace_dstate_percpu_t)); hashsize = size / (dstate->dtds_chunksize + sizeof (dtrace_dynhash_t)); if (hashsize != 1 && (hashsize & 1)) hashsize--; dstate->dtds_hashsize = hashsize; dstate->dtds_hash = dstate->dtds_base; /* * Set all of our hash buckets to point to the single sink, and (if * it hasn't already been set), set the sink's hash value to be the * sink sentinel value. The sink is needed for dynamic variable * lookups to know that they have iterated over an entire, valid hash * chain. */ for (i = 0; i < hashsize; i++) dstate->dtds_hash[i].dtdh_chain = &dtrace_dynhash_sink; if (dtrace_dynhash_sink.dtdv_hashval != DTRACE_DYNHASH_SINK) dtrace_dynhash_sink.dtdv_hashval = DTRACE_DYNHASH_SINK; /* * Determine number of active CPUs. Divide free list evenly among * active CPUs. */ start = (dtrace_dynvar_t *) ((uintptr_t)base + hashsize * sizeof (dtrace_dynhash_t)); limit = (uintptr_t)base + size; maxper = (limit - (uintptr_t)start) / NCPU; maxper = (maxper / dstate->dtds_chunksize) * dstate->dtds_chunksize; #ifndef illumos CPU_FOREACH(i) { #else for (i = 0; i < NCPU; i++) { #endif dstate->dtds_percpu[i].dtdsc_free = dvar = start; /* * If we don't even have enough chunks to make it once through * NCPUs, we're just going to allocate everything to the first * CPU. And if we're on the last CPU, we're going to allocate * whatever is left over. In either case, we set the limit to * be the limit of the dynamic variable space. */ if (maxper == 0 || i == NCPU - 1) { limit = (uintptr_t)base + size; start = NULL; } else { limit = (uintptr_t)start + maxper; start = (dtrace_dynvar_t *)limit; } ASSERT(limit <= (uintptr_t)base + size); for (;;) { next = (dtrace_dynvar_t *)((uintptr_t)dvar + dstate->dtds_chunksize); if ((uintptr_t)next + dstate->dtds_chunksize >= limit) break; dvar->dtdv_next = next; dvar = next; } if (maxper == 0) break; } return (0); } static void dtrace_dstate_fini(dtrace_dstate_t *dstate) { ASSERT(MUTEX_HELD(&cpu_lock)); if (dstate->dtds_base == NULL) return; kmem_free(dstate->dtds_base, dstate->dtds_size); kmem_cache_free(dtrace_state_cache, dstate->dtds_percpu); } static void dtrace_vstate_fini(dtrace_vstate_t *vstate) { /* * Logical XOR, where are you? */ ASSERT((vstate->dtvs_nglobals == 0) ^ (vstate->dtvs_globals != NULL)); if (vstate->dtvs_nglobals > 0) { kmem_free(vstate->dtvs_globals, vstate->dtvs_nglobals * sizeof (dtrace_statvar_t *)); } if (vstate->dtvs_ntlocals > 0) { kmem_free(vstate->dtvs_tlocals, vstate->dtvs_ntlocals * sizeof (dtrace_difv_t)); } ASSERT((vstate->dtvs_nlocals == 0) ^ (vstate->dtvs_locals != NULL)); if (vstate->dtvs_nlocals > 0) { kmem_free(vstate->dtvs_locals, vstate->dtvs_nlocals * sizeof (dtrace_statvar_t *)); } } #ifdef illumos static void dtrace_state_clean(dtrace_state_t *state) { if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) return; dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars); dtrace_speculation_clean(state); } static void dtrace_state_deadman(dtrace_state_t *state) { hrtime_t now; dtrace_sync(); now = dtrace_gethrtime(); if (state != dtrace_anon.dta_state && now - state->dts_laststatus >= dtrace_deadman_user) return; /* * We must be sure that dts_alive never appears to be less than the * value upon entry to dtrace_state_deadman(), and because we lack a * dtrace_cas64(), we cannot store to it atomically. We thus instead * store INT64_MAX to it, followed by a memory barrier, followed by * the new value. This assures that dts_alive never appears to be * less than its true value, regardless of the order in which the * stores to the underlying storage are issued. */ state->dts_alive = INT64_MAX; dtrace_membar_producer(); state->dts_alive = now; } #else /* !illumos */ static void dtrace_state_clean(void *arg) { dtrace_state_t *state = arg; dtrace_optval_t *opt = state->dts_options; if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) return; dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars); dtrace_speculation_clean(state); callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC, dtrace_state_clean, state); } static void dtrace_state_deadman(void *arg) { dtrace_state_t *state = arg; hrtime_t now; dtrace_sync(); dtrace_debug_output(); now = dtrace_gethrtime(); if (state != dtrace_anon.dta_state && now - state->dts_laststatus >= dtrace_deadman_user) return; /* * We must be sure that dts_alive never appears to be less than the * value upon entry to dtrace_state_deadman(), and because we lack a * dtrace_cas64(), we cannot store to it atomically. We thus instead * store INT64_MAX to it, followed by a memory barrier, followed by * the new value. This assures that dts_alive never appears to be * less than its true value, regardless of the order in which the * stores to the underlying storage are issued. */ state->dts_alive = INT64_MAX; dtrace_membar_producer(); state->dts_alive = now; callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC, dtrace_state_deadman, state); } #endif /* illumos */ static dtrace_state_t * #ifdef illumos dtrace_state_create(dev_t *devp, cred_t *cr) #else dtrace_state_create(struct cdev *dev) #endif { #ifdef illumos minor_t minor; major_t major; #else cred_t *cr = NULL; int m = 0; #endif char c[30]; dtrace_state_t *state; dtrace_optval_t *opt; int bufsize = NCPU * sizeof (dtrace_buffer_t), i; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); #ifdef illumos minor = (minor_t)(uintptr_t)vmem_alloc(dtrace_minor, 1, VM_BESTFIT | VM_SLEEP); if (ddi_soft_state_zalloc(dtrace_softstate, minor) != DDI_SUCCESS) { vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1); return (NULL); } state = ddi_get_soft_state(dtrace_softstate, minor); #else if (dev != NULL) { cr = dev->si_cred; m = dev2unit(dev); - } + } /* Allocate memory for the state. */ state = kmem_zalloc(sizeof(dtrace_state_t), KM_SLEEP); #endif state->dts_epid = DTRACE_EPIDNONE + 1; (void) snprintf(c, sizeof (c), "dtrace_aggid_%d", m); #ifdef illumos state->dts_aggid_arena = vmem_create(c, (void *)1, UINT32_MAX, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); if (devp != NULL) { major = getemajor(*devp); } else { major = ddi_driver_major(dtrace_devi); } state->dts_dev = makedevice(major, minor); if (devp != NULL) *devp = state->dts_dev; #else state->dts_aggid_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx); state->dts_dev = dev; #endif /* * We allocate NCPU buffers. On the one hand, this can be quite * a bit of memory per instance (nearly 36K on a Starcat). On the * other hand, it saves an additional memory reference in the probe * path. */ state->dts_buffer = kmem_zalloc(bufsize, KM_SLEEP); state->dts_aggbuffer = kmem_zalloc(bufsize, KM_SLEEP); #ifdef illumos state->dts_cleaner = CYCLIC_NONE; state->dts_deadman = CYCLIC_NONE; #else callout_init(&state->dts_cleaner, CALLOUT_MPSAFE); callout_init(&state->dts_deadman, CALLOUT_MPSAFE); #endif state->dts_vstate.dtvs_state = state; for (i = 0; i < DTRACEOPT_MAX; i++) state->dts_options[i] = DTRACEOPT_UNSET; /* * Set the default options. */ opt = state->dts_options; opt[DTRACEOPT_BUFPOLICY] = DTRACEOPT_BUFPOLICY_SWITCH; opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_AUTO; opt[DTRACEOPT_NSPEC] = dtrace_nspec_default; opt[DTRACEOPT_SPECSIZE] = dtrace_specsize_default; opt[DTRACEOPT_CPU] = (dtrace_optval_t)DTRACE_CPUALL; opt[DTRACEOPT_STRSIZE] = dtrace_strsize_default; opt[DTRACEOPT_STACKFRAMES] = dtrace_stackframes_default; opt[DTRACEOPT_USTACKFRAMES] = dtrace_ustackframes_default; opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_default; opt[DTRACEOPT_AGGRATE] = dtrace_aggrate_default; opt[DTRACEOPT_SWITCHRATE] = dtrace_switchrate_default; opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_default; opt[DTRACEOPT_JSTACKFRAMES] = dtrace_jstackframes_default; opt[DTRACEOPT_JSTACKSTRSIZE] = dtrace_jstackstrsize_default; state->dts_activity = DTRACE_ACTIVITY_INACTIVE; /* * Depending on the user credentials, we set flag bits which alter probe * visibility or the amount of destructiveness allowed. In the case of * actual anonymous tracing, or the possession of all privileges, all of * the normal checks are bypassed. */ if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) { state->dts_cred.dcr_visible = DTRACE_CRV_ALL; state->dts_cred.dcr_action = DTRACE_CRA_ALL; } else { /* * Set up the credentials for this instantiation. We take a * hold on the credential to prevent it from disappearing on * us; this in turn prevents the zone_t referenced by this * credential from disappearing. This means that we can * examine the credential and the zone from probe context. */ crhold(cr); state->dts_cred.dcr_cred = cr; /* * CRA_PROC means "we have *some* privilege for dtrace" and * unlocks the use of variables like pid, zonename, etc. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE) || PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC; } /* * dtrace_user allows use of syscall and profile providers. * If the user also has proc_owner and/or proc_zone, we * extend the scope to include additional visibility and * destructive power. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE)) { if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) { state->dts_cred.dcr_visible |= DTRACE_CRV_ALLPROC; state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; } if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) { state->dts_cred.dcr_visible |= DTRACE_CRV_ALLZONE; state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; } /* * If we have all privs in whatever zone this is, * we can do destructive things to processes which * have altered credentials. */ #ifdef illumos if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE), cr->cr_zone->zone_privset)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG; } #endif } /* * Holding the dtrace_kernel privilege also implies that * the user has the dtrace_user privilege from a visibility * perspective. But without further privileges, some * destructive actions are not available. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE)) { /* * Make all probes in all zones visible. However, * this doesn't mean that all actions become available * to all zones. */ state->dts_cred.dcr_visible |= DTRACE_CRV_KERNEL | DTRACE_CRV_ALLPROC | DTRACE_CRV_ALLZONE; state->dts_cred.dcr_action |= DTRACE_CRA_KERNEL | DTRACE_CRA_PROC; /* * Holding proc_owner means that destructive actions * for *this* zone are allowed. */ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; /* * Holding proc_zone means that destructive actions * for this user/group ID in all zones is allowed. */ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; #ifdef illumos /* * If we have all privs in whatever zone this is, * we can do destructive things to processes which * have altered credentials. */ if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE), cr->cr_zone->zone_privset)) { state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG; } #endif } /* * Holding the dtrace_proc privilege gives control over fasttrap * and pid providers. We need to grant wider destructive * privileges in the event that the user has proc_owner and/or * proc_zone. */ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) { if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER; if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) state->dts_cred.dcr_action |= DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE; } } return (state); } static int dtrace_state_buffer(dtrace_state_t *state, dtrace_buffer_t *buf, int which) { dtrace_optval_t *opt = state->dts_options, size; processorid_t cpu = 0;; int flags = 0, rval, factor, divisor = 1; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(which < DTRACEOPT_MAX); ASSERT(state->dts_activity == DTRACE_ACTIVITY_INACTIVE || (state == dtrace_anon.dta_state && state->dts_activity == DTRACE_ACTIVITY_ACTIVE)); if (opt[which] == DTRACEOPT_UNSET || opt[which] == 0) return (0); if (opt[DTRACEOPT_CPU] != DTRACEOPT_UNSET) cpu = opt[DTRACEOPT_CPU]; if (which == DTRACEOPT_SPECSIZE) flags |= DTRACEBUF_NOSWITCH; if (which == DTRACEOPT_BUFSIZE) { if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_RING) flags |= DTRACEBUF_RING; if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_FILL) flags |= DTRACEBUF_FILL; if (state != dtrace_anon.dta_state || state->dts_activity != DTRACE_ACTIVITY_ACTIVE) flags |= DTRACEBUF_INACTIVE; } for (size = opt[which]; size >= sizeof (uint64_t); size /= divisor) { /* * The size must be 8-byte aligned. If the size is not 8-byte * aligned, drop it down by the difference. */ if (size & (sizeof (uint64_t) - 1)) size -= size & (sizeof (uint64_t) - 1); if (size < state->dts_reserve) { /* * Buffers always must be large enough to accommodate * their prereserved space. We return E2BIG instead * of ENOMEM in this case to allow for user-level * software to differentiate the cases. */ return (E2BIG); } rval = dtrace_buffer_alloc(buf, size, flags, cpu, &factor); if (rval != ENOMEM) { opt[which] = size; return (rval); } if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL) return (rval); for (divisor = 2; divisor < factor; divisor <<= 1) continue; } return (ENOMEM); } static int dtrace_state_buffers(dtrace_state_t *state) { dtrace_speculation_t *spec = state->dts_speculations; int rval, i; if ((rval = dtrace_state_buffer(state, state->dts_buffer, DTRACEOPT_BUFSIZE)) != 0) return (rval); if ((rval = dtrace_state_buffer(state, state->dts_aggbuffer, DTRACEOPT_AGGSIZE)) != 0) return (rval); for (i = 0; i < state->dts_nspeculations; i++) { if ((rval = dtrace_state_buffer(state, spec[i].dtsp_buffer, DTRACEOPT_SPECSIZE)) != 0) return (rval); } return (0); } static void dtrace_state_prereserve(dtrace_state_t *state) { dtrace_ecb_t *ecb; dtrace_probe_t *probe; state->dts_reserve = 0; if (state->dts_options[DTRACEOPT_BUFPOLICY] != DTRACEOPT_BUFPOLICY_FILL) return; /* * If our buffer policy is a "fill" buffer policy, we need to set the * prereserved space to be the space required by the END probes. */ probe = dtrace_probes[dtrace_probeid_end - 1]; ASSERT(probe != NULL); for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) { if (ecb->dte_state != state) continue; state->dts_reserve += ecb->dte_needed + ecb->dte_alignment; } } static int dtrace_state_go(dtrace_state_t *state, processorid_t *cpu) { dtrace_optval_t *opt = state->dts_options, sz, nspec; dtrace_speculation_t *spec; dtrace_buffer_t *buf; #ifdef illumos cyc_handler_t hdlr; cyc_time_t when; #endif int rval = 0, i, bufsize = NCPU * sizeof (dtrace_buffer_t); dtrace_icookie_t cookie; mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) { rval = EBUSY; goto out; } /* * Before we can perform any checks, we must prime all of the * retained enablings that correspond to this state. */ dtrace_enabling_prime(state); if (state->dts_destructive && !state->dts_cred.dcr_destructive) { rval = EACCES; goto out; } dtrace_state_prereserve(state); /* * Now we want to do is try to allocate our speculations. * We do not automatically resize the number of speculations; if * this fails, we will fail the operation. */ nspec = opt[DTRACEOPT_NSPEC]; ASSERT(nspec != DTRACEOPT_UNSET); if (nspec > INT_MAX) { rval = ENOMEM; goto out; } spec = kmem_zalloc(nspec * sizeof (dtrace_speculation_t), KM_NOSLEEP | KM_NORMALPRI); if (spec == NULL) { rval = ENOMEM; goto out; } state->dts_speculations = spec; state->dts_nspeculations = (int)nspec; for (i = 0; i < nspec; i++) { if ((buf = kmem_zalloc(bufsize, KM_NOSLEEP | KM_NORMALPRI)) == NULL) { rval = ENOMEM; goto err; } spec[i].dtsp_buffer = buf; } if (opt[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET) { if (dtrace_anon.dta_state == NULL) { rval = ENOENT; goto out; } if (state->dts_necbs != 0) { rval = EALREADY; goto out; } state->dts_anon = dtrace_anon_grab(); ASSERT(state->dts_anon != NULL); state = state->dts_anon; /* * We want "grabanon" to be set in the grabbed state, so we'll * copy that option value from the grabbing state into the * grabbed state. */ state->dts_options[DTRACEOPT_GRABANON] = opt[DTRACEOPT_GRABANON]; *cpu = dtrace_anon.dta_beganon; /* * If the anonymous state is active (as it almost certainly * is if the anonymous enabling ultimately matched anything), * we don't allow any further option processing -- but we * don't return failure. */ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) goto out; } if (opt[DTRACEOPT_AGGSIZE] != DTRACEOPT_UNSET && opt[DTRACEOPT_AGGSIZE] != 0) { if (state->dts_aggregations == NULL) { /* * We're not going to create an aggregation buffer * because we don't have any ECBs that contain * aggregations -- set this option to 0. */ opt[DTRACEOPT_AGGSIZE] = 0; } else { /* * If we have an aggregation buffer, we must also have * a buffer to use as scratch. */ if (opt[DTRACEOPT_BUFSIZE] == DTRACEOPT_UNSET || opt[DTRACEOPT_BUFSIZE] < state->dts_needed) { opt[DTRACEOPT_BUFSIZE] = state->dts_needed; } } } if (opt[DTRACEOPT_SPECSIZE] != DTRACEOPT_UNSET && opt[DTRACEOPT_SPECSIZE] != 0) { if (!state->dts_speculates) { /* * We're not going to create speculation buffers * because we don't have any ECBs that actually * speculate -- set the speculation size to 0. */ opt[DTRACEOPT_SPECSIZE] = 0; } } /* * The bare minimum size for any buffer that we're actually going to * do anything to is sizeof (uint64_t). */ sz = sizeof (uint64_t); if ((state->dts_needed != 0 && opt[DTRACEOPT_BUFSIZE] < sz) || (state->dts_speculates && opt[DTRACEOPT_SPECSIZE] < sz) || (state->dts_aggregations != NULL && opt[DTRACEOPT_AGGSIZE] < sz)) { /* * A buffer size has been explicitly set to 0 (or to a size * that will be adjusted to 0) and we need the space -- we * need to return failure. We return ENOSPC to differentiate * it from failing to allocate a buffer due to failure to meet * the reserve (for which we return E2BIG). */ rval = ENOSPC; goto out; } if ((rval = dtrace_state_buffers(state)) != 0) goto err; if ((sz = opt[DTRACEOPT_DYNVARSIZE]) == DTRACEOPT_UNSET) sz = dtrace_dstate_defsize; do { rval = dtrace_dstate_init(&state->dts_vstate.dtvs_dynvars, sz); if (rval == 0) break; if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL) goto err; } while (sz >>= 1); opt[DTRACEOPT_DYNVARSIZE] = sz; if (rval != 0) goto err; if (opt[DTRACEOPT_STATUSRATE] > dtrace_statusrate_max) opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_max; if (opt[DTRACEOPT_CLEANRATE] == 0) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max; if (opt[DTRACEOPT_CLEANRATE] < dtrace_cleanrate_min) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_min; if (opt[DTRACEOPT_CLEANRATE] > dtrace_cleanrate_max) opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max; state->dts_alive = state->dts_laststatus = dtrace_gethrtime(); #ifdef illumos hdlr.cyh_func = (cyc_func_t)dtrace_state_clean; hdlr.cyh_arg = state; hdlr.cyh_level = CY_LOW_LEVEL; when.cyt_when = 0; when.cyt_interval = opt[DTRACEOPT_CLEANRATE]; state->dts_cleaner = cyclic_add(&hdlr, &when); hdlr.cyh_func = (cyc_func_t)dtrace_state_deadman; hdlr.cyh_arg = state; hdlr.cyh_level = CY_LOW_LEVEL; when.cyt_when = 0; when.cyt_interval = dtrace_deadman_interval; state->dts_deadman = cyclic_add(&hdlr, &when); #else callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC, dtrace_state_clean, state); callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC, dtrace_state_deadman, state); #endif state->dts_activity = DTRACE_ACTIVITY_WARMUP; #ifdef illumos if (state->dts_getf != 0 && !(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL)) { /* * We don't have kernel privs but we have at least one call * to getf(); we need to bump our zone's count, and (if * this is the first enabling to have an unprivileged call * to getf()) we need to hook into closef(). */ state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf++; if (dtrace_getf++ == 0) { ASSERT(dtrace_closef == NULL); dtrace_closef = dtrace_getf_barrier; } } #endif /* * Now it's time to actually fire the BEGIN probe. We need to disable * interrupts here both to record the CPU on which we fired the BEGIN * probe (the data from this CPU will be processed first at user * level) and to manually activate the buffer for this CPU. */ cookie = dtrace_interrupt_disable(); *cpu = curcpu; ASSERT(state->dts_buffer[*cpu].dtb_flags & DTRACEBUF_INACTIVE); state->dts_buffer[*cpu].dtb_flags &= ~DTRACEBUF_INACTIVE; dtrace_probe(dtrace_probeid_begin, (uint64_t)(uintptr_t)state, 0, 0, 0, 0); dtrace_interrupt_enable(cookie); /* * We may have had an exit action from a BEGIN probe; only change our * state to ACTIVE if we're still in WARMUP. */ ASSERT(state->dts_activity == DTRACE_ACTIVITY_WARMUP || state->dts_activity == DTRACE_ACTIVITY_DRAINING); if (state->dts_activity == DTRACE_ACTIVITY_WARMUP) state->dts_activity = DTRACE_ACTIVITY_ACTIVE; /* * Regardless of whether or not now we're in ACTIVE or DRAINING, we * want each CPU to transition its principal buffer out of the * INACTIVE state. Doing this assures that no CPU will suddenly begin * processing an ECB halfway down a probe's ECB chain; all CPUs will * atomically transition from processing none of a state's ECBs to * processing all of them. */ dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_buffer_activate, state); goto out; err: dtrace_buffer_free(state->dts_buffer); dtrace_buffer_free(state->dts_aggbuffer); if ((nspec = state->dts_nspeculations) == 0) { ASSERT(state->dts_speculations == NULL); goto out; } spec = state->dts_speculations; ASSERT(spec != NULL); for (i = 0; i < state->dts_nspeculations; i++) { if ((buf = spec[i].dtsp_buffer) == NULL) break; dtrace_buffer_free(buf); kmem_free(buf, bufsize); } kmem_free(spec, nspec * sizeof (dtrace_speculation_t)); state->dts_nspeculations = 0; state->dts_speculations = NULL; out: mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (rval); } static int dtrace_state_stop(dtrace_state_t *state, processorid_t *cpu) { dtrace_icookie_t cookie; ASSERT(MUTEX_HELD(&dtrace_lock)); if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE && state->dts_activity != DTRACE_ACTIVITY_DRAINING) return (EINVAL); /* * We'll set the activity to DTRACE_ACTIVITY_DRAINING, and issue a sync * to be sure that every CPU has seen it. See below for the details * on why this is done. */ state->dts_activity = DTRACE_ACTIVITY_DRAINING; dtrace_sync(); /* * By this point, it is impossible for any CPU to be still processing * with DTRACE_ACTIVITY_ACTIVE. We can thus set our activity to * DTRACE_ACTIVITY_COOLDOWN and know that we're not racing with any * other CPU in dtrace_buffer_reserve(). This allows dtrace_probe() * and callees to know that the activity is DTRACE_ACTIVITY_COOLDOWN * iff we're in the END probe. */ state->dts_activity = DTRACE_ACTIVITY_COOLDOWN; dtrace_sync(); ASSERT(state->dts_activity == DTRACE_ACTIVITY_COOLDOWN); /* * Finally, we can release the reserve and call the END probe. We * disable interrupts across calling the END probe to allow us to * return the CPU on which we actually called the END probe. This * allows user-land to be sure that this CPU's principal buffer is * processed last. */ state->dts_reserve = 0; cookie = dtrace_interrupt_disable(); *cpu = curcpu; dtrace_probe(dtrace_probeid_end, (uint64_t)(uintptr_t)state, 0, 0, 0, 0); dtrace_interrupt_enable(cookie); state->dts_activity = DTRACE_ACTIVITY_STOPPED; dtrace_sync(); #ifdef illumos if (state->dts_getf != 0 && !(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL)) { /* * We don't have kernel privs but we have at least one call * to getf(); we need to lower our zone's count, and (if * this is the last enabling to have an unprivileged call * to getf()) we need to clear the closef() hook. */ ASSERT(state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf > 0); ASSERT(dtrace_closef == dtrace_getf_barrier); ASSERT(dtrace_getf > 0); state->dts_cred.dcr_cred->cr_zone->zone_dtrace_getf--; if (--dtrace_getf == 0) dtrace_closef = NULL; } #endif return (0); } static int dtrace_state_option(dtrace_state_t *state, dtrace_optid_t option, dtrace_optval_t val) { ASSERT(MUTEX_HELD(&dtrace_lock)); if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) return (EBUSY); if (option >= DTRACEOPT_MAX) return (EINVAL); if (option != DTRACEOPT_CPU && val < 0) return (EINVAL); switch (option) { case DTRACEOPT_DESTRUCTIVE: if (dtrace_destructive_disallow) return (EACCES); state->dts_cred.dcr_destructive = 1; break; case DTRACEOPT_BUFSIZE: case DTRACEOPT_DYNVARSIZE: case DTRACEOPT_AGGSIZE: case DTRACEOPT_SPECSIZE: case DTRACEOPT_STRSIZE: if (val < 0) return (EINVAL); if (val >= LONG_MAX) { /* * If this is an otherwise negative value, set it to * the highest multiple of 128m less than LONG_MAX. * Technically, we're adjusting the size without * regard to the buffer resizing policy, but in fact, * this has no effect -- if we set the buffer size to * ~LONG_MAX and the buffer policy is ultimately set to * be "manual", the buffer allocation is guaranteed to * fail, if only because the allocation requires two * buffers. (We set the the size to the highest * multiple of 128m because it ensures that the size * will remain a multiple of a megabyte when * repeatedly halved -- all the way down to 15m.) */ val = LONG_MAX - (1 << 27) + 1; } } state->dts_options[option] = val; return (0); } static void dtrace_state_destroy(dtrace_state_t *state) { dtrace_ecb_t *ecb; dtrace_vstate_t *vstate = &state->dts_vstate; #ifdef illumos minor_t minor = getminor(state->dts_dev); #endif int i, bufsize = NCPU * sizeof (dtrace_buffer_t); dtrace_speculation_t *spec = state->dts_speculations; int nspec = state->dts_nspeculations; uint32_t match; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); /* * First, retract any retained enablings for this state. */ dtrace_enabling_retract(state); ASSERT(state->dts_nretained == 0); if (state->dts_activity == DTRACE_ACTIVITY_ACTIVE || state->dts_activity == DTRACE_ACTIVITY_DRAINING) { /* * We have managed to come into dtrace_state_destroy() on a * hot enabling -- almost certainly because of a disorderly * shutdown of a consumer. (That is, a consumer that is * exiting without having called dtrace_stop().) In this case, * we're going to set our activity to be KILLED, and then * issue a sync to be sure that everyone is out of probe * context before we start blowing away ECBs. */ state->dts_activity = DTRACE_ACTIVITY_KILLED; dtrace_sync(); } /* * Release the credential hold we took in dtrace_state_create(). */ if (state->dts_cred.dcr_cred != NULL) crfree(state->dts_cred.dcr_cred); /* * Now we can safely disable and destroy any enabled probes. Because * any DTRACE_PRIV_KERNEL probes may actually be slowing our progress * (especially if they're all enabled), we take two passes through the * ECBs: in the first, we disable just DTRACE_PRIV_KERNEL probes, and * in the second we disable whatever is left over. */ for (match = DTRACE_PRIV_KERNEL; ; match = 0) { for (i = 0; i < state->dts_necbs; i++) { if ((ecb = state->dts_ecbs[i]) == NULL) continue; if (match && ecb->dte_probe != NULL) { dtrace_probe_t *probe = ecb->dte_probe; dtrace_provider_t *prov = probe->dtpr_provider; if (!(prov->dtpv_priv.dtpp_flags & match)) continue; } dtrace_ecb_disable(ecb); dtrace_ecb_destroy(ecb); } if (!match) break; } /* * Before we free the buffers, perform one more sync to assure that * every CPU is out of probe context. */ dtrace_sync(); dtrace_buffer_free(state->dts_buffer); dtrace_buffer_free(state->dts_aggbuffer); for (i = 0; i < nspec; i++) dtrace_buffer_free(spec[i].dtsp_buffer); #ifdef illumos if (state->dts_cleaner != CYCLIC_NONE) cyclic_remove(state->dts_cleaner); if (state->dts_deadman != CYCLIC_NONE) cyclic_remove(state->dts_deadman); #else callout_stop(&state->dts_cleaner); callout_drain(&state->dts_cleaner); callout_stop(&state->dts_deadman); callout_drain(&state->dts_deadman); #endif dtrace_dstate_fini(&vstate->dtvs_dynvars); dtrace_vstate_fini(vstate); if (state->dts_ecbs != NULL) kmem_free(state->dts_ecbs, state->dts_necbs * sizeof (dtrace_ecb_t *)); if (state->dts_aggregations != NULL) { #ifdef DEBUG for (i = 0; i < state->dts_naggregations; i++) ASSERT(state->dts_aggregations[i] == NULL); #endif ASSERT(state->dts_naggregations > 0); kmem_free(state->dts_aggregations, state->dts_naggregations * sizeof (dtrace_aggregation_t *)); } kmem_free(state->dts_buffer, bufsize); kmem_free(state->dts_aggbuffer, bufsize); for (i = 0; i < nspec; i++) kmem_free(spec[i].dtsp_buffer, bufsize); if (spec != NULL) kmem_free(spec, nspec * sizeof (dtrace_speculation_t)); dtrace_format_destroy(state); if (state->dts_aggid_arena != NULL) { #ifdef illumos vmem_destroy(state->dts_aggid_arena); #else delete_unrhdr(state->dts_aggid_arena); #endif state->dts_aggid_arena = NULL; } #ifdef illumos ddi_soft_state_free(dtrace_softstate, minor); vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1); #endif } /* * DTrace Anonymous Enabling Functions */ static dtrace_state_t * dtrace_anon_grab(void) { dtrace_state_t *state; ASSERT(MUTEX_HELD(&dtrace_lock)); if ((state = dtrace_anon.dta_state) == NULL) { ASSERT(dtrace_anon.dta_enabling == NULL); return (NULL); } ASSERT(dtrace_anon.dta_enabling != NULL); ASSERT(dtrace_retained != NULL); dtrace_enabling_destroy(dtrace_anon.dta_enabling); dtrace_anon.dta_enabling = NULL; dtrace_anon.dta_state = NULL; return (state); } static void dtrace_anon_property(void) { int i, rv; dtrace_state_t *state; dof_hdr_t *dof; char c[32]; /* enough for "dof-data-" + digits */ ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(MUTEX_HELD(&cpu_lock)); for (i = 0; ; i++) { (void) snprintf(c, sizeof (c), "dof-data-%d", i); dtrace_err_verbose = 1; if ((dof = dtrace_dof_property(c)) == NULL) { dtrace_err_verbose = 0; break; } #ifdef illumos /* * We want to create anonymous state, so we need to transition * the kernel debugger to indicate that DTrace is active. If * this fails (e.g. because the debugger has modified text in * some way), we won't continue with the processing. */ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) { cmn_err(CE_NOTE, "kernel debugger active; anonymous " "enabling ignored."); dtrace_dof_destroy(dof); break; } #endif /* * If we haven't allocated an anonymous state, we'll do so now. */ if ((state = dtrace_anon.dta_state) == NULL) { #ifdef illumos state = dtrace_state_create(NULL, NULL); #else state = dtrace_state_create(NULL); #endif dtrace_anon.dta_state = state; if (state == NULL) { /* * This basically shouldn't happen: the only * failure mode from dtrace_state_create() is a * failure of ddi_soft_state_zalloc() that * itself should never happen. Still, the * interface allows for a failure mode, and * we want to fail as gracefully as possible: * we'll emit an error message and cease * processing anonymous state in this case. */ cmn_err(CE_WARN, "failed to create " "anonymous state"); dtrace_dof_destroy(dof); break; } } rv = dtrace_dof_slurp(dof, &state->dts_vstate, CRED(), &dtrace_anon.dta_enabling, 0, B_TRUE); if (rv == 0) rv = dtrace_dof_options(dof, state); dtrace_err_verbose = 0; dtrace_dof_destroy(dof); if (rv != 0) { /* * This is malformed DOF; chuck any anonymous state * that we created. */ ASSERT(dtrace_anon.dta_enabling == NULL); dtrace_state_destroy(state); dtrace_anon.dta_state = NULL; break; } ASSERT(dtrace_anon.dta_enabling != NULL); } if (dtrace_anon.dta_enabling != NULL) { int rval; /* * dtrace_enabling_retain() can only fail because we are * trying to retain more enablings than are allowed -- but * we only have one anonymous enabling, and we are guaranteed * to be allowed at least one retained enabling; we assert * that dtrace_enabling_retain() returns success. */ rval = dtrace_enabling_retain(dtrace_anon.dta_enabling); ASSERT(rval == 0); dtrace_enabling_dump(dtrace_anon.dta_enabling); } } /* * DTrace Helper Functions */ static void dtrace_helper_trace(dtrace_helper_action_t *helper, dtrace_mstate_t *mstate, dtrace_vstate_t *vstate, int where) { uint32_t size, next, nnext, i; - dtrace_helptrace_t *ent; + dtrace_helptrace_t *ent, *buffer; uint16_t flags = cpu_core[curcpu].cpuc_dtrace_flags; - if (!dtrace_helptrace_enabled) + if ((buffer = dtrace_helptrace_buffer) == NULL) return; ASSERT(vstate->dtvs_nlocals <= dtrace_helptrace_nlocals); /* * What would a tracing framework be without its own tracing * framework? (Well, a hell of a lot simpler, for starters...) */ size = sizeof (dtrace_helptrace_t) + dtrace_helptrace_nlocals * sizeof (uint64_t) - sizeof (uint64_t); /* * Iterate until we can allocate a slot in the trace buffer. */ do { next = dtrace_helptrace_next; if (next + size < dtrace_helptrace_bufsize) { nnext = next + size; } else { nnext = size; } } while (dtrace_cas32(&dtrace_helptrace_next, next, nnext) != next); /* * We have our slot; fill it in. */ - if (nnext == size) + if (nnext == size) { + dtrace_helptrace_wrapped++; next = 0; + } - ent = (dtrace_helptrace_t *)&dtrace_helptrace_buffer[next]; + ent = (dtrace_helptrace_t *)((uintptr_t)buffer + next); ent->dtht_helper = helper; ent->dtht_where = where; ent->dtht_nlocals = vstate->dtvs_nlocals; ent->dtht_fltoffs = (mstate->dtms_present & DTRACE_MSTATE_FLTOFFS) ? mstate->dtms_fltoffs : -1; ent->dtht_fault = DTRACE_FLAGS2FLT(flags); ent->dtht_illval = cpu_core[curcpu].cpuc_dtrace_illval; for (i = 0; i < vstate->dtvs_nlocals; i++) { dtrace_statvar_t *svar; if ((svar = vstate->dtvs_locals[i]) == NULL) continue; ASSERT(svar->dtsv_size >= NCPU * sizeof (uint64_t)); ent->dtht_locals[i] = ((uint64_t *)(uintptr_t)svar->dtsv_data)[curcpu]; } } static uint64_t dtrace_helper(int which, dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t arg0, uint64_t arg1) { uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags; uint64_t sarg0 = mstate->dtms_arg[0]; uint64_t sarg1 = mstate->dtms_arg[1]; uint64_t rval = 0; dtrace_helpers_t *helpers = curproc->p_dtrace_helpers; dtrace_helper_action_t *helper; dtrace_vstate_t *vstate; dtrace_difo_t *pred; - int i, trace = dtrace_helptrace_enabled; + int i, trace = dtrace_helptrace_buffer != NULL; ASSERT(which >= 0 && which < DTRACE_NHELPER_ACTIONS); if (helpers == NULL) return (0); if ((helper = helpers->dthps_actions[which]) == NULL) return (0); vstate = &helpers->dthps_vstate; mstate->dtms_arg[0] = arg0; mstate->dtms_arg[1] = arg1; /* * Now iterate over each helper. If its predicate evaluates to 'true', * we'll call the corresponding actions. Note that the below calls * to dtrace_dif_emulate() may set faults in machine state. This is * okay: our caller (the outer dtrace_dif_emulate()) will simply plow * the stored DIF offset with its own (which is the desired behavior). * Also, note the calls to dtrace_dif_emulate() may allocate scratch * from machine state; this is okay, too. */ for (; helper != NULL; helper = helper->dtha_next) { if ((pred = helper->dtha_predicate) != NULL) { if (trace) dtrace_helper_trace(helper, mstate, vstate, 0); if (!dtrace_dif_emulate(pred, mstate, vstate, state)) goto next; if (*flags & CPU_DTRACE_FAULT) goto err; } for (i = 0; i < helper->dtha_nactions; i++) { if (trace) dtrace_helper_trace(helper, mstate, vstate, i + 1); rval = dtrace_dif_emulate(helper->dtha_actions[i], mstate, vstate, state); if (*flags & CPU_DTRACE_FAULT) goto err; } next: if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_NEXT); } if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_DONE); /* * Restore the arg0 that we saved upon entry. */ mstate->dtms_arg[0] = sarg0; mstate->dtms_arg[1] = sarg1; return (rval); err: if (trace) dtrace_helper_trace(helper, mstate, vstate, DTRACE_HELPTRACE_ERR); /* * Restore the arg0 that we saved upon entry. */ mstate->dtms_arg[0] = sarg0; mstate->dtms_arg[1] = sarg1; return (0); } static void dtrace_helper_action_destroy(dtrace_helper_action_t *helper, dtrace_vstate_t *vstate) { int i; if (helper->dtha_predicate != NULL) dtrace_difo_release(helper->dtha_predicate, vstate); for (i = 0; i < helper->dtha_nactions; i++) { ASSERT(helper->dtha_actions[i] != NULL); dtrace_difo_release(helper->dtha_actions[i], vstate); } kmem_free(helper->dtha_actions, helper->dtha_nactions * sizeof (dtrace_difo_t *)); kmem_free(helper, sizeof (dtrace_helper_action_t)); } static int dtrace_helper_destroygen(int gen) { proc_t *p = curproc; dtrace_helpers_t *help = p->p_dtrace_helpers; dtrace_vstate_t *vstate; int i; ASSERT(MUTEX_HELD(&dtrace_lock)); if (help == NULL || gen > help->dthps_generation) return (EINVAL); vstate = &help->dthps_vstate; for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { dtrace_helper_action_t *last = NULL, *h, *next; for (h = help->dthps_actions[i]; h != NULL; h = next) { next = h->dtha_next; if (h->dtha_generation == gen) { if (last != NULL) { last->dtha_next = next; } else { help->dthps_actions[i] = next; } dtrace_helper_action_destroy(h, vstate); } else { last = h; } } } /* * Interate until we've cleared out all helper providers with the * given generation number. */ for (;;) { dtrace_helper_provider_t *prov; /* * Look for a helper provider with the right generation. We * have to start back at the beginning of the list each time * because we drop dtrace_lock. It's unlikely that we'll make * more than two passes. */ for (i = 0; i < help->dthps_nprovs; i++) { prov = help->dthps_provs[i]; if (prov->dthp_generation == gen) break; } /* * If there were no matches, we're done. */ if (i == help->dthps_nprovs) break; /* * Move the last helper provider into this slot. */ help->dthps_nprovs--; help->dthps_provs[i] = help->dthps_provs[help->dthps_nprovs]; help->dthps_provs[help->dthps_nprovs] = NULL; mutex_exit(&dtrace_lock); /* * If we have a meta provider, remove this helper provider. */ mutex_enter(&dtrace_meta_lock); if (dtrace_meta_pid != NULL) { ASSERT(dtrace_deferred_pid == NULL); dtrace_helper_provider_remove(&prov->dthp_prov, p->p_pid); } mutex_exit(&dtrace_meta_lock); dtrace_helper_provider_destroy(prov); mutex_enter(&dtrace_lock); } return (0); } static int dtrace_helper_validate(dtrace_helper_action_t *helper) { int err = 0, i; dtrace_difo_t *dp; if ((dp = helper->dtha_predicate) != NULL) err += dtrace_difo_validate_helper(dp); for (i = 0; i < helper->dtha_nactions; i++) err += dtrace_difo_validate_helper(helper->dtha_actions[i]); return (err == 0); } static int dtrace_helper_action_add(int which, dtrace_ecbdesc_t *ep) { dtrace_helpers_t *help; dtrace_helper_action_t *helper, *last; dtrace_actdesc_t *act; dtrace_vstate_t *vstate; dtrace_predicate_t *pred; int count = 0, nactions = 0, i; if (which < 0 || which >= DTRACE_NHELPER_ACTIONS) return (EINVAL); help = curproc->p_dtrace_helpers; last = help->dthps_actions[which]; vstate = &help->dthps_vstate; for (count = 0; last != NULL; last = last->dtha_next) { count++; if (last->dtha_next == NULL) break; } /* * If we already have dtrace_helper_actions_max helper actions for this * helper action type, we'll refuse to add a new one. */ if (count >= dtrace_helper_actions_max) return (ENOSPC); helper = kmem_zalloc(sizeof (dtrace_helper_action_t), KM_SLEEP); helper->dtha_generation = help->dthps_generation; if ((pred = ep->dted_pred.dtpdd_predicate) != NULL) { ASSERT(pred->dtp_difo != NULL); dtrace_difo_hold(pred->dtp_difo); helper->dtha_predicate = pred->dtp_difo; } for (act = ep->dted_action; act != NULL; act = act->dtad_next) { if (act->dtad_kind != DTRACEACT_DIFEXPR) goto err; if (act->dtad_difo == NULL) goto err; nactions++; } helper->dtha_actions = kmem_zalloc(sizeof (dtrace_difo_t *) * (helper->dtha_nactions = nactions), KM_SLEEP); for (act = ep->dted_action, i = 0; act != NULL; act = act->dtad_next) { dtrace_difo_hold(act->dtad_difo); helper->dtha_actions[i++] = act->dtad_difo; } if (!dtrace_helper_validate(helper)) goto err; if (last == NULL) { help->dthps_actions[which] = helper; } else { last->dtha_next = helper; } if (vstate->dtvs_nlocals > dtrace_helptrace_nlocals) { dtrace_helptrace_nlocals = vstate->dtvs_nlocals; dtrace_helptrace_next = 0; } return (0); err: dtrace_helper_action_destroy(helper, vstate); return (EINVAL); } static void dtrace_helper_provider_register(proc_t *p, dtrace_helpers_t *help, dof_helper_t *dofhp) { ASSERT(MUTEX_NOT_HELD(&dtrace_lock)); mutex_enter(&dtrace_meta_lock); mutex_enter(&dtrace_lock); if (!dtrace_attached() || dtrace_meta_pid == NULL) { /* * If the dtrace module is loaded but not attached, or if * there aren't isn't a meta provider registered to deal with * these provider descriptions, we need to postpone creating * the actual providers until later. */ if (help->dthps_next == NULL && help->dthps_prev == NULL && dtrace_deferred_pid != help) { help->dthps_deferred = 1; help->dthps_pid = p->p_pid; help->dthps_next = dtrace_deferred_pid; help->dthps_prev = NULL; if (dtrace_deferred_pid != NULL) dtrace_deferred_pid->dthps_prev = help; dtrace_deferred_pid = help; } mutex_exit(&dtrace_lock); } else if (dofhp != NULL) { /* * If the dtrace module is loaded and we have a particular * helper provider description, pass that off to the * meta provider. */ mutex_exit(&dtrace_lock); dtrace_helper_provide(dofhp, p->p_pid); } else { /* * Otherwise, just pass all the helper provider descriptions * off to the meta provider. */ int i; mutex_exit(&dtrace_lock); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov, p->p_pid); } } mutex_exit(&dtrace_meta_lock); } static int dtrace_helper_provider_add(dof_helper_t *dofhp, int gen) { dtrace_helpers_t *help; dtrace_helper_provider_t *hprov, **tmp_provs; uint_t tmp_maxprovs, i; ASSERT(MUTEX_HELD(&dtrace_lock)); help = curproc->p_dtrace_helpers; ASSERT(help != NULL); /* * If we already have dtrace_helper_providers_max helper providers, * we're refuse to add a new one. */ if (help->dthps_nprovs >= dtrace_helper_providers_max) return (ENOSPC); /* * Check to make sure this isn't a duplicate. */ for (i = 0; i < help->dthps_nprovs; i++) { if (dofhp->dofhp_dof == help->dthps_provs[i]->dthp_prov.dofhp_dof) return (EALREADY); } hprov = kmem_zalloc(sizeof (dtrace_helper_provider_t), KM_SLEEP); hprov->dthp_prov = *dofhp; hprov->dthp_ref = 1; hprov->dthp_generation = gen; /* * Allocate a bigger table for helper providers if it's already full. */ if (help->dthps_maxprovs == help->dthps_nprovs) { tmp_maxprovs = help->dthps_maxprovs; tmp_provs = help->dthps_provs; if (help->dthps_maxprovs == 0) help->dthps_maxprovs = 2; else help->dthps_maxprovs *= 2; if (help->dthps_maxprovs > dtrace_helper_providers_max) help->dthps_maxprovs = dtrace_helper_providers_max; ASSERT(tmp_maxprovs < help->dthps_maxprovs); help->dthps_provs = kmem_zalloc(help->dthps_maxprovs * sizeof (dtrace_helper_provider_t *), KM_SLEEP); if (tmp_provs != NULL) { bcopy(tmp_provs, help->dthps_provs, tmp_maxprovs * sizeof (dtrace_helper_provider_t *)); kmem_free(tmp_provs, tmp_maxprovs * sizeof (dtrace_helper_provider_t *)); } } help->dthps_provs[help->dthps_nprovs] = hprov; help->dthps_nprovs++; return (0); } static void dtrace_helper_provider_destroy(dtrace_helper_provider_t *hprov) { mutex_enter(&dtrace_lock); if (--hprov->dthp_ref == 0) { dof_hdr_t *dof; mutex_exit(&dtrace_lock); dof = (dof_hdr_t *)(uintptr_t)hprov->dthp_prov.dofhp_dof; dtrace_dof_destroy(dof); kmem_free(hprov, sizeof (dtrace_helper_provider_t)); } else { mutex_exit(&dtrace_lock); } } static int dtrace_helper_provider_validate(dof_hdr_t *dof, dof_sec_t *sec) { uintptr_t daddr = (uintptr_t)dof; dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec; dof_provider_t *provider; dof_probe_t *probe; uint8_t *arg; char *strtab, *typestr; dof_stridx_t typeidx; size_t typesz; uint_t nprobes, j, k; ASSERT(sec->dofs_type == DOF_SECT_PROVIDER); if (sec->dofs_offset & (sizeof (uint_t) - 1)) { dtrace_dof_error(dof, "misaligned section offset"); return (-1); } /* * The section needs to be large enough to contain the DOF provider * structure appropriate for the given version. */ if (sec->dofs_size < ((dof->dofh_ident[DOF_ID_VERSION] == DOF_VERSION_1) ? offsetof(dof_provider_t, dofpv_prenoffs) : sizeof (dof_provider_t))) { dtrace_dof_error(dof, "provider section too small"); return (-1); } provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset); str_sec = dtrace_dof_sect(dof, DOF_SECT_STRTAB, provider->dofpv_strtab); prb_sec = dtrace_dof_sect(dof, DOF_SECT_PROBES, provider->dofpv_probes); arg_sec = dtrace_dof_sect(dof, DOF_SECT_PRARGS, provider->dofpv_prargs); off_sec = dtrace_dof_sect(dof, DOF_SECT_PROFFS, provider->dofpv_proffs); if (str_sec == NULL || prb_sec == NULL || arg_sec == NULL || off_sec == NULL) return (-1); enoff_sec = NULL; if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 && provider->dofpv_prenoffs != DOF_SECT_NONE && (enoff_sec = dtrace_dof_sect(dof, DOF_SECT_PRENOFFS, provider->dofpv_prenoffs)) == NULL) return (-1); strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset); if (provider->dofpv_name >= str_sec->dofs_size || strlen(strtab + provider->dofpv_name) >= DTRACE_PROVNAMELEN) { dtrace_dof_error(dof, "invalid provider name"); return (-1); } if (prb_sec->dofs_entsize == 0 || prb_sec->dofs_entsize > prb_sec->dofs_size) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } if (prb_sec->dofs_entsize & (sizeof (uintptr_t) - 1)) { dtrace_dof_error(dof, "misaligned entry size"); return (-1); } if (off_sec->dofs_entsize != sizeof (uint32_t)) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } if (off_sec->dofs_offset & (sizeof (uint32_t) - 1)) { dtrace_dof_error(dof, "misaligned section offset"); return (-1); } if (arg_sec->dofs_entsize != sizeof (uint8_t)) { dtrace_dof_error(dof, "invalid entry size"); return (-1); } arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset); nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize; /* * Take a pass through the probes to check for errors. */ for (j = 0; j < nprobes; j++) { probe = (dof_probe_t *)(uintptr_t)(daddr + prb_sec->dofs_offset + j * prb_sec->dofs_entsize); if (probe->dofpr_func >= str_sec->dofs_size) { dtrace_dof_error(dof, "invalid function name"); return (-1); } if (strlen(strtab + probe->dofpr_func) >= DTRACE_FUNCNAMELEN) { dtrace_dof_error(dof, "function name too long"); return (-1); } if (probe->dofpr_name >= str_sec->dofs_size || strlen(strtab + probe->dofpr_name) >= DTRACE_NAMELEN) { dtrace_dof_error(dof, "invalid probe name"); return (-1); } /* * The offset count must not wrap the index, and the offsets * must also not overflow the section's data. */ if (probe->dofpr_offidx + probe->dofpr_noffs < probe->dofpr_offidx || (probe->dofpr_offidx + probe->dofpr_noffs) * off_sec->dofs_entsize > off_sec->dofs_size) { dtrace_dof_error(dof, "invalid probe offset"); return (-1); } if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1) { /* * If there's no is-enabled offset section, make sure * there aren't any is-enabled offsets. Otherwise * perform the same checks as for probe offsets * (immediately above). */ if (enoff_sec == NULL) { if (probe->dofpr_enoffidx != 0 || probe->dofpr_nenoffs != 0) { dtrace_dof_error(dof, "is-enabled " "offsets with null section"); return (-1); } } else if (probe->dofpr_enoffidx + probe->dofpr_nenoffs < probe->dofpr_enoffidx || (probe->dofpr_enoffidx + probe->dofpr_nenoffs) * enoff_sec->dofs_entsize > enoff_sec->dofs_size) { dtrace_dof_error(dof, "invalid is-enabled " "offset"); return (-1); } if (probe->dofpr_noffs + probe->dofpr_nenoffs == 0) { dtrace_dof_error(dof, "zero probe and " "is-enabled offsets"); return (-1); } } else if (probe->dofpr_noffs == 0) { dtrace_dof_error(dof, "zero probe offsets"); return (-1); } if (probe->dofpr_argidx + probe->dofpr_xargc < probe->dofpr_argidx || (probe->dofpr_argidx + probe->dofpr_xargc) * arg_sec->dofs_entsize > arg_sec->dofs_size) { dtrace_dof_error(dof, "invalid args"); return (-1); } typeidx = probe->dofpr_nargv; typestr = strtab + probe->dofpr_nargv; for (k = 0; k < probe->dofpr_nargc; k++) { if (typeidx >= str_sec->dofs_size) { dtrace_dof_error(dof, "bad " "native argument type"); return (-1); } typesz = strlen(typestr) + 1; if (typesz > DTRACE_ARGTYPELEN) { dtrace_dof_error(dof, "native " "argument type too long"); return (-1); } typeidx += typesz; typestr += typesz; } typeidx = probe->dofpr_xargv; typestr = strtab + probe->dofpr_xargv; for (k = 0; k < probe->dofpr_xargc; k++) { if (arg[probe->dofpr_argidx + k] > probe->dofpr_nargc) { dtrace_dof_error(dof, "bad " "native argument index"); return (-1); } if (typeidx >= str_sec->dofs_size) { dtrace_dof_error(dof, "bad " "translated argument type"); return (-1); } typesz = strlen(typestr) + 1; if (typesz > DTRACE_ARGTYPELEN) { dtrace_dof_error(dof, "translated argument " "type too long"); return (-1); } typeidx += typesz; typestr += typesz; } } return (0); } static int dtrace_helper_slurp(dof_hdr_t *dof, dof_helper_t *dhp) { dtrace_helpers_t *help; dtrace_vstate_t *vstate; dtrace_enabling_t *enab = NULL; int i, gen, rv, nhelpers = 0, nprovs = 0, destroy = 1; uintptr_t daddr = (uintptr_t)dof; ASSERT(MUTEX_HELD(&dtrace_lock)); if ((help = curproc->p_dtrace_helpers) == NULL) help = dtrace_helpers_create(curproc); vstate = &help->dthps_vstate; if ((rv = dtrace_dof_slurp(dof, vstate, NULL, &enab, dhp != NULL ? dhp->dofhp_addr : 0, B_FALSE)) != 0) { dtrace_dof_destroy(dof); return (rv); } /* * Look for helper providers and validate their descriptions. */ if (dhp != NULL) { for (i = 0; i < dof->dofh_secnum; i++) { dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff + i * dof->dofh_secsize); if (sec->dofs_type != DOF_SECT_PROVIDER) continue; if (dtrace_helper_provider_validate(dof, sec) != 0) { dtrace_enabling_destroy(enab); dtrace_dof_destroy(dof); return (-1); } nprovs++; } } /* * Now we need to walk through the ECB descriptions in the enabling. */ for (i = 0; i < enab->dten_ndesc; i++) { dtrace_ecbdesc_t *ep = enab->dten_desc[i]; dtrace_probedesc_t *desc = &ep->dted_probe; if (strcmp(desc->dtpd_provider, "dtrace") != 0) continue; if (strcmp(desc->dtpd_mod, "helper") != 0) continue; if (strcmp(desc->dtpd_func, "ustack") != 0) continue; if ((rv = dtrace_helper_action_add(DTRACE_HELPER_ACTION_USTACK, ep)) != 0) { /* * Adding this helper action failed -- we are now going * to rip out the entire generation and return failure. */ (void) dtrace_helper_destroygen(help->dthps_generation); dtrace_enabling_destroy(enab); dtrace_dof_destroy(dof); return (-1); } nhelpers++; } if (nhelpers < enab->dten_ndesc) dtrace_dof_error(dof, "unmatched helpers"); gen = help->dthps_generation++; dtrace_enabling_destroy(enab); if (dhp != NULL && nprovs > 0) { dhp->dofhp_dof = (uint64_t)(uintptr_t)dof; if (dtrace_helper_provider_add(dhp, gen) == 0) { mutex_exit(&dtrace_lock); dtrace_helper_provider_register(curproc, help, dhp); mutex_enter(&dtrace_lock); destroy = 0; } } if (destroy) dtrace_dof_destroy(dof); return (gen); } static dtrace_helpers_t * dtrace_helpers_create(proc_t *p) { dtrace_helpers_t *help; ASSERT(MUTEX_HELD(&dtrace_lock)); ASSERT(p->p_dtrace_helpers == NULL); help = kmem_zalloc(sizeof (dtrace_helpers_t), KM_SLEEP); help->dthps_actions = kmem_zalloc(sizeof (dtrace_helper_action_t *) * DTRACE_NHELPER_ACTIONS, KM_SLEEP); p->p_dtrace_helpers = help; dtrace_helpers++; return (help); } #ifdef illumos static #endif void dtrace_helpers_destroy(proc_t *p) { dtrace_helpers_t *help; dtrace_vstate_t *vstate; #ifdef illumos proc_t *p = curproc; #endif int i; mutex_enter(&dtrace_lock); ASSERT(p->p_dtrace_helpers != NULL); ASSERT(dtrace_helpers > 0); help = p->p_dtrace_helpers; vstate = &help->dthps_vstate; /* * We're now going to lose the help from this process. */ p->p_dtrace_helpers = NULL; dtrace_sync(); /* * Destory the helper actions. */ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { dtrace_helper_action_t *h, *next; for (h = help->dthps_actions[i]; h != NULL; h = next) { next = h->dtha_next; dtrace_helper_action_destroy(h, vstate); h = next; } } mutex_exit(&dtrace_lock); /* * Destroy the helper providers. */ if (help->dthps_maxprovs > 0) { mutex_enter(&dtrace_meta_lock); if (dtrace_meta_pid != NULL) { ASSERT(dtrace_deferred_pid == NULL); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provider_remove( &help->dthps_provs[i]->dthp_prov, p->p_pid); } } else { mutex_enter(&dtrace_lock); ASSERT(help->dthps_deferred == 0 || help->dthps_next != NULL || help->dthps_prev != NULL || help == dtrace_deferred_pid); /* * Remove the helper from the deferred list. */ if (help->dthps_next != NULL) help->dthps_next->dthps_prev = help->dthps_prev; if (help->dthps_prev != NULL) help->dthps_prev->dthps_next = help->dthps_next; if (dtrace_deferred_pid == help) { dtrace_deferred_pid = help->dthps_next; ASSERT(help->dthps_prev == NULL); } mutex_exit(&dtrace_lock); } mutex_exit(&dtrace_meta_lock); for (i = 0; i < help->dthps_nprovs; i++) { dtrace_helper_provider_destroy(help->dthps_provs[i]); } kmem_free(help->dthps_provs, help->dthps_maxprovs * sizeof (dtrace_helper_provider_t *)); } mutex_enter(&dtrace_lock); dtrace_vstate_fini(&help->dthps_vstate); kmem_free(help->dthps_actions, sizeof (dtrace_helper_action_t *) * DTRACE_NHELPER_ACTIONS); kmem_free(help, sizeof (dtrace_helpers_t)); --dtrace_helpers; mutex_exit(&dtrace_lock); } #ifdef illumos static #endif void dtrace_helpers_duplicate(proc_t *from, proc_t *to) { dtrace_helpers_t *help, *newhelp; dtrace_helper_action_t *helper, *new, *last; dtrace_difo_t *dp; dtrace_vstate_t *vstate; int i, j, sz, hasprovs = 0; mutex_enter(&dtrace_lock); ASSERT(from->p_dtrace_helpers != NULL); ASSERT(dtrace_helpers > 0); help = from->p_dtrace_helpers; newhelp = dtrace_helpers_create(to); ASSERT(to->p_dtrace_helpers != NULL); newhelp->dthps_generation = help->dthps_generation; vstate = &newhelp->dthps_vstate; /* * Duplicate the helper actions. */ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { if ((helper = help->dthps_actions[i]) == NULL) continue; for (last = NULL; helper != NULL; helper = helper->dtha_next) { new = kmem_zalloc(sizeof (dtrace_helper_action_t), KM_SLEEP); new->dtha_generation = helper->dtha_generation; if ((dp = helper->dtha_predicate) != NULL) { dp = dtrace_difo_duplicate(dp, vstate); new->dtha_predicate = dp; } new->dtha_nactions = helper->dtha_nactions; sz = sizeof (dtrace_difo_t *) * new->dtha_nactions; new->dtha_actions = kmem_alloc(sz, KM_SLEEP); for (j = 0; j < new->dtha_nactions; j++) { dtrace_difo_t *dp = helper->dtha_actions[j]; ASSERT(dp != NULL); dp = dtrace_difo_duplicate(dp, vstate); new->dtha_actions[j] = dp; } if (last != NULL) { last->dtha_next = new; } else { newhelp->dthps_actions[i] = new; } last = new; } } /* * Duplicate the helper providers and register them with the * DTrace framework. */ if (help->dthps_nprovs > 0) { newhelp->dthps_nprovs = help->dthps_nprovs; newhelp->dthps_maxprovs = help->dthps_nprovs; newhelp->dthps_provs = kmem_alloc(newhelp->dthps_nprovs * sizeof (dtrace_helper_provider_t *), KM_SLEEP); for (i = 0; i < newhelp->dthps_nprovs; i++) { newhelp->dthps_provs[i] = help->dthps_provs[i]; newhelp->dthps_provs[i]->dthp_ref++; } hasprovs = 1; } mutex_exit(&dtrace_lock); if (hasprovs) dtrace_helper_provider_register(to, newhelp, NULL); } /* * DTrace Hook Functions */ static void dtrace_module_loaded(modctl_t *ctl) { dtrace_provider_t *prv; mutex_enter(&dtrace_provider_lock); #ifdef illumos mutex_enter(&mod_lock); #endif #ifdef illumos ASSERT(ctl->mod_busy); #endif /* * We're going to call each providers per-module provide operation * specifying only this module. */ for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next) prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); /* * If we have any retained enablings, we need to match against them. * Enabling probes requires that cpu_lock be held, and we cannot hold * cpu_lock here -- it is legal for cpu_lock to be held when loading a * module. (In particular, this happens when loading scheduling * classes.) So if we have any retained enablings, we need to dispatch * our task queue to do the match for us. */ mutex_enter(&dtrace_lock); if (dtrace_retained == NULL) { mutex_exit(&dtrace_lock); return; } (void) taskq_dispatch(dtrace_taskq, (task_func_t *)dtrace_enabling_matchall, NULL, TQ_SLEEP); mutex_exit(&dtrace_lock); /* * And now, for a little heuristic sleaze: in general, we want to * match modules as soon as they load. However, we cannot guarantee * this, because it would lead us to the lock ordering violation * outlined above. The common case, of course, is that cpu_lock is * _not_ held -- so we delay here for a clock tick, hoping that that's * long enough for the task queue to do its work. If it's not, it's * not a serious problem -- it just means that the module that we * just loaded may not be immediately instrumentable. */ delay(1); } static void #ifdef illumos dtrace_module_unloaded(modctl_t *ctl) #else dtrace_module_unloaded(modctl_t *ctl, int *error) #endif { dtrace_probe_t template, *probe, *first, *next; dtrace_provider_t *prov; #ifndef illumos char modname[DTRACE_MODNAMELEN]; size_t len; #endif #ifdef illumos template.dtpr_mod = ctl->mod_modname; #else /* Handle the fact that ctl->filename may end in ".ko". */ strlcpy(modname, ctl->filename, sizeof(modname)); len = strlen(ctl->filename); if (len > 3 && strcmp(modname + len - 3, ".ko") == 0) modname[len - 3] = '\0'; template.dtpr_mod = modname; #endif mutex_enter(&dtrace_provider_lock); #ifdef illumos mutex_enter(&mod_lock); #endif mutex_enter(&dtrace_lock); #ifndef illumos if (ctl->nenabled > 0) { /* Don't allow unloads if a probe is enabled. */ mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); *error = -1; printf( "kldunload: attempt to unload module that has DTrace probes enabled\n"); return; } #endif if (dtrace_bymod == NULL) { /* * The DTrace module is loaded (obviously) but not attached; * we don't have any work to do. */ mutex_exit(&dtrace_provider_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_lock); return; } for (probe = first = dtrace_hash_lookup(dtrace_bymod, &template); probe != NULL; probe = probe->dtpr_nextmod) { if (probe->dtpr_ecb != NULL) { mutex_exit(&dtrace_provider_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_lock); /* * This shouldn't _actually_ be possible -- we're * unloading a module that has an enabled probe in it. * (It's normally up to the provider to make sure that * this can't happen.) However, because dtps_enable() * doesn't have a failure mode, there can be an * enable/unload race. Upshot: we don't want to * assert, but we're not going to disable the * probe, either. */ if (dtrace_err_verbose) { #ifdef illumos cmn_err(CE_WARN, "unloaded module '%s' had " "enabled probes", ctl->mod_modname); #else cmn_err(CE_WARN, "unloaded module '%s' had " "enabled probes", modname); #endif } return; } } probe = first; for (first = NULL; probe != NULL; probe = next) { ASSERT(dtrace_probes[probe->dtpr_id - 1] == probe); dtrace_probes[probe->dtpr_id - 1] = NULL; next = probe->dtpr_nextmod; dtrace_hash_remove(dtrace_bymod, probe); dtrace_hash_remove(dtrace_byfunc, probe); dtrace_hash_remove(dtrace_byname, probe); if (first == NULL) { first = probe; probe->dtpr_nextmod = NULL; } else { probe->dtpr_nextmod = first; first = probe; } } /* * We've removed all of the module's probes from the hash chains and * from the probe array. Now issue a dtrace_sync() to be sure that * everyone has cleared out from any probe array processing. */ dtrace_sync(); for (probe = first; probe != NULL; probe = first) { first = probe->dtpr_nextmod; prov = probe->dtpr_provider; prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg); kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1); kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1); kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1); #ifdef illumos vmem_free(dtrace_arena, (void *)(uintptr_t)probe->dtpr_id, 1); #else free_unr(dtrace_arena, probe->dtpr_id); #endif kmem_free(probe, sizeof (dtrace_probe_t)); } mutex_exit(&dtrace_lock); #ifdef illumos mutex_exit(&mod_lock); #endif mutex_exit(&dtrace_provider_lock); } #ifndef illumos static void dtrace_kld_load(void *arg __unused, linker_file_t lf) { dtrace_module_loaded(lf); } static void dtrace_kld_unload_try(void *arg __unused, linker_file_t lf, int *error) { if (*error != 0) /* We already have an error, so don't do anything. */ return; dtrace_module_unloaded(lf, error); } #endif #ifdef illumos static void dtrace_suspend(void) { dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_suspend)); } static void dtrace_resume(void) { dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_resume)); } #endif static int dtrace_cpu_setup(cpu_setup_t what, processorid_t cpu) { ASSERT(MUTEX_HELD(&cpu_lock)); mutex_enter(&dtrace_lock); switch (what) { case CPU_CONFIG: { dtrace_state_t *state; dtrace_optval_t *opt, rs, c; /* * For now, we only allocate a new buffer for anonymous state. */ if ((state = dtrace_anon.dta_state) == NULL) break; if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE) break; opt = state->dts_options; c = opt[DTRACEOPT_CPU]; if (c != DTRACE_CPUALL && c != DTRACEOPT_UNSET && c != cpu) break; /* * Regardless of what the actual policy is, we're going to * temporarily set our resize policy to be manual. We're * also going to temporarily set our CPU option to denote * the newly configured CPU. */ rs = opt[DTRACEOPT_BUFRESIZE]; opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_MANUAL; opt[DTRACEOPT_CPU] = (dtrace_optval_t)cpu; (void) dtrace_state_buffers(state); opt[DTRACEOPT_BUFRESIZE] = rs; opt[DTRACEOPT_CPU] = c; break; } case CPU_UNCONFIG: /* * We don't free the buffer in the CPU_UNCONFIG case. (The * buffer will be freed when the consumer exits.) */ break; default: break; } mutex_exit(&dtrace_lock); return (0); } #ifdef illumos static void dtrace_cpu_setup_initial(processorid_t cpu) { (void) dtrace_cpu_setup(CPU_CONFIG, cpu); } #endif static void dtrace_toxrange_add(uintptr_t base, uintptr_t limit) { if (dtrace_toxranges >= dtrace_toxranges_max) { int osize, nsize; dtrace_toxrange_t *range; osize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t); if (osize == 0) { ASSERT(dtrace_toxrange == NULL); ASSERT(dtrace_toxranges_max == 0); dtrace_toxranges_max = 1; } else { dtrace_toxranges_max <<= 1; } nsize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t); range = kmem_zalloc(nsize, KM_SLEEP); if (dtrace_toxrange != NULL) { ASSERT(osize != 0); bcopy(dtrace_toxrange, range, osize); kmem_free(dtrace_toxrange, osize); } dtrace_toxrange = range; } ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_base == 0); ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_limit == 0); dtrace_toxrange[dtrace_toxranges].dtt_base = base; dtrace_toxrange[dtrace_toxranges].dtt_limit = limit; dtrace_toxranges++; } static void dtrace_getf_barrier() { #ifdef illumos /* * When we have unprivileged (that is, non-DTRACE_CRV_KERNEL) enablings * that contain calls to getf(), this routine will be called on every * closef() before either the underlying vnode is released or the * file_t itself is freed. By the time we are here, it is essential * that the file_t can no longer be accessed from a call to getf() * in probe context -- that assures that a dtrace_sync() can be used * to clear out any enablings referring to the old structures. */ if (curthread->t_procp->p_zone->zone_dtrace_getf != 0 || kcred->cr_zone->zone_dtrace_getf != 0) dtrace_sync(); #endif } /* * DTrace Driver Cookbook Functions */ #ifdef illumos /*ARGSUSED*/ static int dtrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { dtrace_provider_id_t id; dtrace_state_t *state = NULL; dtrace_enabling_t *enab; mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); if (ddi_soft_state_init(&dtrace_softstate, sizeof (dtrace_state_t), 0) != 0) { cmn_err(CE_NOTE, "/dev/dtrace failed to initialize soft state"); mutex_exit(&cpu_lock); mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); return (DDI_FAILURE); } if (ddi_create_minor_node(devi, DTRACEMNR_DTRACE, S_IFCHR, DTRACEMNRN_DTRACE, DDI_PSEUDO, NULL) == DDI_FAILURE || ddi_create_minor_node(devi, DTRACEMNR_HELPER, S_IFCHR, DTRACEMNRN_HELPER, DDI_PSEUDO, NULL) == DDI_FAILURE) { cmn_err(CE_NOTE, "/dev/dtrace couldn't create minor nodes"); ddi_remove_minor_node(devi, NULL); ddi_soft_state_fini(&dtrace_softstate); mutex_exit(&cpu_lock); mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); return (DDI_FAILURE); } ddi_report_dev(devi); dtrace_devi = devi; dtrace_modload = dtrace_module_loaded; dtrace_modunload = dtrace_module_unloaded; dtrace_cpu_init = dtrace_cpu_setup_initial; dtrace_helpers_cleanup = dtrace_helpers_destroy; dtrace_helpers_fork = dtrace_helpers_duplicate; dtrace_cpustart_init = dtrace_suspend; dtrace_cpustart_fini = dtrace_resume; dtrace_debugger_init = dtrace_suspend; dtrace_debugger_fini = dtrace_resume; register_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_arena = vmem_create("dtrace", (void *)1, UINT32_MAX, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); dtrace_minor = vmem_create("dtrace_minor", (void *)DTRACEMNRN_CLONE, UINT32_MAX - DTRACEMNRN_CLONE, 1, NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER); dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri, 1, INT_MAX, 0); dtrace_state_cache = kmem_cache_create("dtrace_state_cache", sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod), offsetof(dtrace_probe_t, dtpr_nextmod), offsetof(dtrace_probe_t, dtpr_prevmod)); dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func), offsetof(dtrace_probe_t, dtpr_nextfunc), offsetof(dtrace_probe_t, dtpr_prevfunc)); dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name), offsetof(dtrace_probe_t, dtpr_nextname), offsetof(dtrace_probe_t, dtpr_prevname)); if (dtrace_retain_max < 1) { cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; " "setting to 1", dtrace_retain_max); dtrace_retain_max = 1; } /* * Now discover our toxic ranges. */ dtrace_toxic_ranges(dtrace_toxrange_add); /* * Before we register ourselves as a provider to our own framework, * we would like to assert that dtrace_provider is NULL -- but that's * not true if we were loaded as a dependency of a DTrace provider. * Once we've registered, we can assert that dtrace_provider is our * pseudo provider. */ (void) dtrace_register("dtrace", &dtrace_provider_attr, DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id); ASSERT(dtrace_provider != NULL); ASSERT((dtrace_provider_id_t)dtrace_provider == id); dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "BEGIN", 0, NULL); dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "END", 0, NULL); dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "ERROR", 1, NULL); dtrace_anon_property(); mutex_exit(&cpu_lock); /* - * If DTrace helper tracing is enabled, we need to allocate the - * trace buffer and initialize the values. - */ - if (dtrace_helptrace_enabled) { - ASSERT(dtrace_helptrace_buffer == NULL); - dtrace_helptrace_buffer = - kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP); - dtrace_helptrace_next = 0; - } - - /* * If there are already providers, we must ask them to provide their * probes, and then match any anonymous enabling against them. Note * that there should be no other retained enablings at this time: * the only retained enablings at this time should be the anonymous * enabling. */ if (dtrace_anon.dta_enabling != NULL) { ASSERT(dtrace_retained == dtrace_anon.dta_enabling); dtrace_enabling_provide(NULL); state = dtrace_anon.dta_state; /* * We couldn't hold cpu_lock across the above call to * dtrace_enabling_provide(), but we must hold it to actually * enable the probes. We have to drop all of our locks, pick * up cpu_lock, and regain our locks before matching the * retained anonymous enabling. */ mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); if ((enab = dtrace_anon.dta_enabling) != NULL) (void) dtrace_enabling_match(enab, NULL); mutex_exit(&cpu_lock); } mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); if (state != NULL) { /* * If we created any anonymous state, set it going now. */ (void) dtrace_state_go(state, &dtrace_anon.dta_beganon); } return (DDI_SUCCESS); } #endif /* illumos */ #ifndef illumos static void dtrace_dtr(void *); #endif /*ARGSUSED*/ static int #ifdef illumos dtrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) #else dtrace_open(struct cdev *dev, int oflags, int devtype, struct thread *td) #endif { dtrace_state_t *state; uint32_t priv; uid_t uid; zoneid_t zoneid; #ifdef illumos if (getminor(*devp) == DTRACEMNRN_HELPER) return (0); /* * If this wasn't an open with the "helper" minor, then it must be * the "dtrace" minor. */ if (getminor(*devp) == DTRACEMNRN_DTRACE) return (ENXIO); #else cred_t *cred_p = NULL; cred_p = dev->si_cred; /* * If no DTRACE_PRIV_* bits are set in the credential, then the * caller lacks sufficient permission to do anything with DTrace. */ dtrace_cred2priv(cred_p, &priv, &uid, &zoneid); if (priv == DTRACE_PRIV_NONE) { #endif return (EACCES); } /* * Ask all providers to provide all their probes. */ mutex_enter(&dtrace_provider_lock); dtrace_probe_provide(NULL, NULL); mutex_exit(&dtrace_provider_lock); mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); dtrace_opens++; dtrace_membar_producer(); #ifdef illumos /* * If the kernel debugger is active (that is, if the kernel debugger * modified text in some way), we won't allow the open. */ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) { dtrace_opens--; mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); return (EBUSY); } + if (dtrace_helptrace_enable && dtrace_helptrace_buffer == NULL) { + /* + * If DTrace helper tracing is enabled, we need to allocate the + * trace buffer and initialize the values. + */ + dtrace_helptrace_buffer = + kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP); + dtrace_helptrace_next = 0; + dtrace_helptrace_wrapped = 0; + dtrace_helptrace_enable = 0; + } + state = dtrace_state_create(devp, cred_p); #else state = dtrace_state_create(dev); devfs_set_cdevpriv(state, dtrace_dtr); #endif mutex_exit(&cpu_lock); if (state == NULL) { #ifdef illumos if (--dtrace_opens == 0 && dtrace_anon.dta_enabling == NULL) (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); #else --dtrace_opens; #endif mutex_exit(&dtrace_lock); return (EAGAIN); } mutex_exit(&dtrace_lock); return (0); } /*ARGSUSED*/ #ifdef illumos static int dtrace_close(dev_t dev, int flag, int otyp, cred_t *cred_p) #else static void dtrace_dtr(void *data) #endif { #ifdef illumos minor_t minor = getminor(dev); dtrace_state_t *state; +#endif + dtrace_helptrace_t *buf = NULL; +#ifdef illumos if (minor == DTRACEMNRN_HELPER) return (0); state = ddi_get_soft_state(dtrace_softstate, minor); #else dtrace_state_t *state = data; #endif mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); - if (state != NULL) { - if (state->dts_anon) { - /* - * There is anonymous state. Destroy that first. - */ - ASSERT(dtrace_anon.dta_state == NULL); - dtrace_state_destroy(state->dts_anon); - } +#ifdef illumos + if (state->dts_anon) +#else + if (state != NULL && state->dts_anon) +#endif + { + /* + * There is anonymous state. Destroy that first. + */ + ASSERT(dtrace_anon.dta_state == NULL); + dtrace_state_destroy(state->dts_anon); + } - dtrace_state_destroy(state); + if (dtrace_helptrace_disable) { + /* + * If we have been told to disable helper tracing, set the + * buffer to NULL before calling into dtrace_state_destroy(); + * we take advantage of its dtrace_sync() to know that no + * CPU is in probe context with enabled helper tracing + * after it returns. + */ + buf = dtrace_helptrace_buffer; + dtrace_helptrace_buffer = NULL; + } -#ifndef illumos +#ifdef illumos + dtrace_state_destroy(state); +#else + if (state == NULL) { + dtrace_state_destroy(state); kmem_free(state, 0); -#endif } - +#endif ASSERT(dtrace_opens > 0); + #ifdef illumos /* * Only relinquish control of the kernel debugger interface when there * are no consumers and no anonymous enablings. */ if (--dtrace_opens == 0 && dtrace_anon.dta_enabling == NULL) (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); #else --dtrace_opens; #endif + if (buf != NULL) { + kmem_free(buf, dtrace_helptrace_bufsize); + dtrace_helptrace_disable = 0; + } + mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); #ifdef illumos return (0); #endif } #ifdef illumos /*ARGSUSED*/ static int dtrace_ioctl_helper(int cmd, intptr_t arg, int *rv) { int rval; dof_helper_t help, *dhp = NULL; switch (cmd) { case DTRACEHIOC_ADDDOF: if (copyin((void *)arg, &help, sizeof (help)) != 0) { dtrace_dof_error(NULL, "failed to copyin DOF helper"); return (EFAULT); } dhp = &help; arg = (intptr_t)help.dofhp_dof; /*FALLTHROUGH*/ case DTRACEHIOC_ADD: { dof_hdr_t *dof = dtrace_dof_copyin(arg, &rval); if (dof == NULL) return (rval); mutex_enter(&dtrace_lock); /* * dtrace_helper_slurp() takes responsibility for the dof -- * it may free it now or it may save it and free it later. */ if ((rval = dtrace_helper_slurp(dof, dhp)) != -1) { *rv = rval; rval = 0; } else { rval = EINVAL; } mutex_exit(&dtrace_lock); return (rval); } case DTRACEHIOC_REMOVE: { mutex_enter(&dtrace_lock); rval = dtrace_helper_destroygen(arg); mutex_exit(&dtrace_lock); return (rval); } default: break; } return (ENOTTY); } /*ARGSUSED*/ static int dtrace_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) { minor_t minor = getminor(dev); dtrace_state_t *state; int rval; if (minor == DTRACEMNRN_HELPER) return (dtrace_ioctl_helper(cmd, arg, rv)); state = ddi_get_soft_state(dtrace_softstate, minor); if (state->dts_anon) { ASSERT(dtrace_anon.dta_state == NULL); state = state->dts_anon; } switch (cmd) { case DTRACEIOC_PROVIDER: { dtrace_providerdesc_t pvd; dtrace_provider_t *pvp; if (copyin((void *)arg, &pvd, sizeof (pvd)) != 0) return (EFAULT); pvd.dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0'; mutex_enter(&dtrace_provider_lock); for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) { if (strcmp(pvp->dtpv_name, pvd.dtvd_name) == 0) break; } mutex_exit(&dtrace_provider_lock); if (pvp == NULL) return (ESRCH); bcopy(&pvp->dtpv_priv, &pvd.dtvd_priv, sizeof (dtrace_ppriv_t)); bcopy(&pvp->dtpv_attr, &pvd.dtvd_attr, sizeof (dtrace_pattr_t)); if (copyout(&pvd, (void *)arg, sizeof (pvd)) != 0) return (EFAULT); return (0); } case DTRACEIOC_EPROBE: { dtrace_eprobedesc_t epdesc; dtrace_ecb_t *ecb; dtrace_action_t *act; void *buf; size_t size; uintptr_t dest; int nrecs; if (copyin((void *)arg, &epdesc, sizeof (epdesc)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } if (ecb->dte_probe == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id; epdesc.dtepd_uarg = ecb->dte_uarg; epdesc.dtepd_size = ecb->dte_size; nrecs = epdesc.dtepd_nrecs; epdesc.dtepd_nrecs = 0; for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple) continue; epdesc.dtepd_nrecs++; } /* * Now that we have the size, we need to allocate a temporary * buffer in which to store the complete description. We need * the temporary buffer to be able to drop dtrace_lock() * across the copyout(), below. */ size = sizeof (dtrace_eprobedesc_t) + (epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t)); buf = kmem_alloc(size, KM_SLEEP); dest = (uintptr_t)buf; bcopy(&epdesc, (void *)dest, sizeof (epdesc)); dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]); for (act = ecb->dte_action; act != NULL; act = act->dta_next) { if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple) continue; if (nrecs-- == 0) break; bcopy(&act->dta_rec, (void *)dest, sizeof (dtrace_recdesc_t)); dest += sizeof (dtrace_recdesc_t); } mutex_exit(&dtrace_lock); if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) { kmem_free(buf, size); return (EFAULT); } kmem_free(buf, size); return (0); } case DTRACEIOC_AGGDESC: { dtrace_aggdesc_t aggdesc; dtrace_action_t *act; dtrace_aggregation_t *agg; int nrecs; uint32_t offs; dtrace_recdesc_t *lrec; void *buf; size_t size; uintptr_t dest; if (copyin((void *)arg, &aggdesc, sizeof (aggdesc)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) { mutex_exit(&dtrace_lock); return (EINVAL); } aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid; nrecs = aggdesc.dtagd_nrecs; aggdesc.dtagd_nrecs = 0; offs = agg->dtag_base; lrec = &agg->dtag_action.dta_rec; aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs; for (act = agg->dtag_first; ; act = act->dta_next) { ASSERT(act->dta_intuple || DTRACEACT_ISAGG(act->dta_kind)); /* * If this action has a record size of zero, it * denotes an argument to the aggregating action. * Because the presence of this record doesn't (or * shouldn't) affect the way the data is interpreted, * we don't copy it out to save user-level the * confusion of dealing with a zero-length record. */ if (act->dta_rec.dtrd_size == 0) { ASSERT(agg->dtag_hasarg); continue; } aggdesc.dtagd_nrecs++; if (act == &agg->dtag_action) break; } /* * Now that we have the size, we need to allocate a temporary * buffer in which to store the complete description. We need * the temporary buffer to be able to drop dtrace_lock() * across the copyout(), below. */ size = sizeof (dtrace_aggdesc_t) + (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t)); buf = kmem_alloc(size, KM_SLEEP); dest = (uintptr_t)buf; bcopy(&aggdesc, (void *)dest, sizeof (aggdesc)); dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]); for (act = agg->dtag_first; ; act = act->dta_next) { dtrace_recdesc_t rec = act->dta_rec; /* * See the comment in the above loop for why we pass * over zero-length records. */ if (rec.dtrd_size == 0) { ASSERT(agg->dtag_hasarg); continue; } if (nrecs-- == 0) break; rec.dtrd_offset -= offs; bcopy(&rec, (void *)dest, sizeof (rec)); dest += sizeof (dtrace_recdesc_t); if (act == &agg->dtag_action) break; } mutex_exit(&dtrace_lock); if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) { kmem_free(buf, size); return (EFAULT); } kmem_free(buf, size); return (0); } case DTRACEIOC_ENABLE: { dof_hdr_t *dof; dtrace_enabling_t *enab = NULL; dtrace_vstate_t *vstate; int err = 0; *rv = 0; /* * If a NULL argument has been passed, we take this as our * cue to reevaluate our enablings. */ if (arg == NULL) { dtrace_enabling_matchall(); return (0); } if ((dof = dtrace_dof_copyin(arg, &rval)) == NULL) return (rval); mutex_enter(&cpu_lock); mutex_enter(&dtrace_lock); vstate = &state->dts_vstate; if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) { mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (EBUSY); } if (dtrace_dof_slurp(dof, vstate, cr, &enab, 0, B_TRUE) != 0) { mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (EINVAL); } if ((rval = dtrace_dof_options(dof, state)) != 0) { dtrace_enabling_destroy(enab); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); dtrace_dof_destroy(dof); return (rval); } if ((err = dtrace_enabling_match(enab, rv)) == 0) { err = dtrace_enabling_retain(enab); } else { dtrace_enabling_destroy(enab); } mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); dtrace_dof_destroy(dof); return (err); } case DTRACEIOC_REPLICATE: { dtrace_repldesc_t desc; dtrace_probedesc_t *match = &desc.dtrpd_match; dtrace_probedesc_t *create = &desc.dtrpd_create; int err; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; match->dtpd_name[DTRACE_NAMELEN - 1] = '\0'; create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; create->dtpd_name[DTRACE_NAMELEN - 1] = '\0'; mutex_enter(&dtrace_lock); err = dtrace_enabling_replicate(state, match, create); mutex_exit(&dtrace_lock); return (err); } case DTRACEIOC_PROBEMATCH: case DTRACEIOC_PROBES: { dtrace_probe_t *probe = NULL; dtrace_probedesc_t desc; dtrace_probekey_t pkey; dtrace_id_t i; int m = 0; uint32_t priv; uid_t uid; zoneid_t zoneid; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); desc.dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0'; desc.dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0'; desc.dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0'; desc.dtpd_name[DTRACE_NAMELEN - 1] = '\0'; /* * Before we attempt to match this probe, we want to give * all providers the opportunity to provide it. */ if (desc.dtpd_id == DTRACE_IDNONE) { mutex_enter(&dtrace_provider_lock); dtrace_probe_provide(&desc, NULL); mutex_exit(&dtrace_provider_lock); desc.dtpd_id++; } if (cmd == DTRACEIOC_PROBEMATCH) { dtrace_probekey(&desc, &pkey); pkey.dtpk_id = DTRACE_IDNONE; } dtrace_cred2priv(cr, &priv, &uid, &zoneid); mutex_enter(&dtrace_lock); if (cmd == DTRACEIOC_PROBEMATCH) { for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) { if ((probe = dtrace_probes[i - 1]) != NULL && (m = dtrace_match_probe(probe, &pkey, priv, uid, zoneid)) != 0) break; } if (m < 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } else { for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) { if ((probe = dtrace_probes[i - 1]) != NULL && dtrace_match_priv(probe, priv, uid, zoneid)) break; } } if (probe == NULL) { mutex_exit(&dtrace_lock); return (ESRCH); } dtrace_probe_description(probe, &desc); mutex_exit(&dtrace_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_PROBEARG: { dtrace_argdesc_t desc; dtrace_probe_t *probe; dtrace_provider_t *prov; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); if (desc.dtargd_id == DTRACE_IDNONE) return (EINVAL); if (desc.dtargd_ndx == DTRACE_ARGNONE) return (EINVAL); mutex_enter(&dtrace_provider_lock); mutex_enter(&mod_lock); mutex_enter(&dtrace_lock); if (desc.dtargd_id > dtrace_nprobes) { mutex_exit(&dtrace_lock); mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); return (EINVAL); } if ((probe = dtrace_probes[desc.dtargd_id - 1]) == NULL) { mutex_exit(&dtrace_lock); mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); return (EINVAL); } mutex_exit(&dtrace_lock); prov = probe->dtpr_provider; if (prov->dtpv_pops.dtps_getargdesc == NULL) { /* * There isn't any typed information for this probe. * Set the argument number to DTRACE_ARGNONE. */ desc.dtargd_ndx = DTRACE_ARGNONE; } else { desc.dtargd_native[0] = '\0'; desc.dtargd_xlate[0] = '\0'; desc.dtargd_mapping = desc.dtargd_ndx; prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg, probe->dtpr_id, probe->dtpr_arg, &desc); } mutex_exit(&mod_lock); mutex_exit(&dtrace_provider_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_GO: { processorid_t cpuid; rval = dtrace_state_go(state, &cpuid); if (rval != 0) return (rval); if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0) return (EFAULT); return (0); } case DTRACEIOC_STOP: { processorid_t cpuid; mutex_enter(&dtrace_lock); rval = dtrace_state_stop(state, &cpuid); mutex_exit(&dtrace_lock); if (rval != 0) return (rval); if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0) return (EFAULT); return (0); } case DTRACEIOC_DOFGET: { dof_hdr_t hdr, *dof; uint64_t len; if (copyin((void *)arg, &hdr, sizeof (hdr)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); dof = dtrace_dof_create(state); mutex_exit(&dtrace_lock); len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz); rval = copyout(dof, (void *)arg, len); dtrace_dof_destroy(dof); return (rval == 0 ? 0 : EFAULT); } case DTRACEIOC_AGGSNAP: case DTRACEIOC_BUFSNAP: { dtrace_bufdesc_t desc; caddr_t cached; dtrace_buffer_t *buf; if (copyin((void *)arg, &desc, sizeof (desc)) != 0) return (EFAULT); if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU) return (EINVAL); mutex_enter(&dtrace_lock); if (cmd == DTRACEIOC_BUFSNAP) { buf = &state->dts_buffer[desc.dtbd_cpu]; } else { buf = &state->dts_aggbuffer[desc.dtbd_cpu]; } if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) { size_t sz = buf->dtb_offset; if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) { mutex_exit(&dtrace_lock); return (EBUSY); } /* * If this buffer has already been consumed, we're * going to indicate that there's nothing left here * to consume. */ if (buf->dtb_flags & DTRACEBUF_CONSUMED) { mutex_exit(&dtrace_lock); desc.dtbd_size = 0; desc.dtbd_drops = 0; desc.dtbd_errors = 0; desc.dtbd_oldest = 0; sz = sizeof (desc); if (copyout(&desc, (void *)arg, sz) != 0) return (EFAULT); return (0); } /* * If this is a ring buffer that has wrapped, we want * to copy the whole thing out. */ if (buf->dtb_flags & DTRACEBUF_WRAPPED) { dtrace_buffer_polish(buf); sz = buf->dtb_size; } if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) { mutex_exit(&dtrace_lock); return (EFAULT); } desc.dtbd_size = sz; desc.dtbd_drops = buf->dtb_drops; desc.dtbd_errors = buf->dtb_errors; desc.dtbd_oldest = buf->dtb_xamot_offset; desc.dtbd_timestamp = dtrace_gethrtime(); mutex_exit(&dtrace_lock); if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); buf->dtb_flags |= DTRACEBUF_CONSUMED; return (0); } if (buf->dtb_tomax == NULL) { ASSERT(buf->dtb_xamot == NULL); mutex_exit(&dtrace_lock); return (ENOENT); } cached = buf->dtb_tomax; ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH)); dtrace_xcall(desc.dtbd_cpu, (dtrace_xcall_t)dtrace_buffer_switch, buf); state->dts_errors += buf->dtb_xamot_errors; /* * If the buffers did not actually switch, then the cross call * did not take place -- presumably because the given CPU is * not in the ready set. If this is the case, we'll return * ENOENT. */ if (buf->dtb_tomax == cached) { ASSERT(buf->dtb_xamot != cached); mutex_exit(&dtrace_lock); return (ENOENT); } ASSERT(cached == buf->dtb_xamot); /* * We have our snapshot; now copy it out. */ if (copyout(buf->dtb_xamot, desc.dtbd_data, buf->dtb_xamot_offset) != 0) { mutex_exit(&dtrace_lock); return (EFAULT); } desc.dtbd_size = buf->dtb_xamot_offset; desc.dtbd_drops = buf->dtb_xamot_drops; desc.dtbd_errors = buf->dtb_xamot_errors; desc.dtbd_oldest = 0; desc.dtbd_timestamp = buf->dtb_switched; mutex_exit(&dtrace_lock); /* * Finally, copy out the buffer description. */ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0) return (EFAULT); return (0); } case DTRACEIOC_CONF: { dtrace_conf_t conf; bzero(&conf, sizeof (conf)); conf.dtc_difversion = DIF_VERSION; conf.dtc_difintregs = DIF_DIR_NREGS; conf.dtc_diftupregs = DIF_DTR_NREGS; conf.dtc_ctfmodel = CTF_MODEL_NATIVE; if (copyout(&conf, (void *)arg, sizeof (conf)) != 0) return (EFAULT); return (0); } case DTRACEIOC_STATUS: { dtrace_status_t stat; dtrace_dstate_t *dstate; int i, j; uint64_t nerrs; /* * See the comment in dtrace_state_deadman() for the reason * for setting dts_laststatus to INT64_MAX before setting * it to the correct value. */ state->dts_laststatus = INT64_MAX; dtrace_membar_producer(); state->dts_laststatus = dtrace_gethrtime(); bzero(&stat, sizeof (stat)); mutex_enter(&dtrace_lock); if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) { mutex_exit(&dtrace_lock); return (ENOENT); } if (state->dts_activity == DTRACE_ACTIVITY_DRAINING) stat.dtst_exiting = 1; nerrs = state->dts_errors; dstate = &state->dts_vstate.dtvs_dynvars; for (i = 0; i < NCPU; i++) { dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i]; stat.dtst_dyndrops += dcpu->dtdsc_drops; stat.dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops; stat.dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops; if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL) stat.dtst_filled++; nerrs += state->dts_buffer[i].dtb_errors; for (j = 0; j < state->dts_nspeculations; j++) { dtrace_speculation_t *spec; dtrace_buffer_t *buf; spec = &state->dts_speculations[j]; buf = &spec->dtsp_buffer[i]; stat.dtst_specdrops += buf->dtb_xamot_drops; } } stat.dtst_specdrops_busy = state->dts_speculations_busy; stat.dtst_specdrops_unavail = state->dts_speculations_unavail; stat.dtst_stkstroverflows = state->dts_stkstroverflows; stat.dtst_dblerrors = state->dts_dblerrors; stat.dtst_killed = (state->dts_activity == DTRACE_ACTIVITY_KILLED); stat.dtst_errors = nerrs; mutex_exit(&dtrace_lock); if (copyout(&stat, (void *)arg, sizeof (stat)) != 0) return (EFAULT); return (0); } case DTRACEIOC_FORMAT: { dtrace_fmtdesc_t fmt; char *str; int len; if (copyin((void *)arg, &fmt, sizeof (fmt)) != 0) return (EFAULT); mutex_enter(&dtrace_lock); if (fmt.dtfd_format == 0 || fmt.dtfd_format > state->dts_nformats) { mutex_exit(&dtrace_lock); return (EINVAL); } /* * Format strings are allocated contiguously and they are * never freed; if a format index is less than the number * of formats, we can assert that the format map is non-NULL * and that the format for the specified index is non-NULL. */ ASSERT(state->dts_formats != NULL); str = state->dts_formats[fmt.dtfd_format - 1]; ASSERT(str != NULL); len = strlen(str) + 1; if (len > fmt.dtfd_length) { fmt.dtfd_length = len; if (copyout(&fmt, (void *)arg, sizeof (fmt)) != 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } else { if (copyout(str, fmt.dtfd_string, len) != 0) { mutex_exit(&dtrace_lock); return (EINVAL); } } mutex_exit(&dtrace_lock); return (0); } default: break; } return (ENOTTY); } /*ARGSUSED*/ static int dtrace_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { dtrace_state_t *state; switch (cmd) { case DDI_DETACH: break; case DDI_SUSPEND: return (DDI_SUCCESS); default: return (DDI_FAILURE); } mutex_enter(&cpu_lock); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); ASSERT(dtrace_opens == 0); if (dtrace_helpers > 0) { mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (DDI_FAILURE); } if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) { mutex_exit(&dtrace_provider_lock); mutex_exit(&dtrace_lock); mutex_exit(&cpu_lock); return (DDI_FAILURE); } dtrace_provider = NULL; if ((state = dtrace_anon_grab()) != NULL) { /* * If there were ECBs on this state, the provider should * have not been allowed to detach; assert that there is * none. */ ASSERT(state->dts_necbs == 0); dtrace_state_destroy(state); /* * If we're being detached with anonymous state, we need to * indicate to the kernel debugger that DTrace is now inactive. */ (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE); } bzero(&dtrace_anon, sizeof (dtrace_anon_t)); unregister_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL); dtrace_cpu_init = NULL; dtrace_helpers_cleanup = NULL; dtrace_helpers_fork = NULL; dtrace_cpustart_init = NULL; dtrace_cpustart_fini = NULL; dtrace_debugger_init = NULL; dtrace_debugger_fini = NULL; dtrace_modload = NULL; dtrace_modunload = NULL; ASSERT(dtrace_getf == 0); ASSERT(dtrace_closef == NULL); mutex_exit(&cpu_lock); - - if (dtrace_helptrace_enabled) { - kmem_free(dtrace_helptrace_buffer, dtrace_helptrace_bufsize); - dtrace_helptrace_buffer = NULL; - } kmem_free(dtrace_probes, dtrace_nprobes * sizeof (dtrace_probe_t *)); dtrace_probes = NULL; dtrace_nprobes = 0; dtrace_hash_destroy(dtrace_bymod); dtrace_hash_destroy(dtrace_byfunc); dtrace_hash_destroy(dtrace_byname); dtrace_bymod = NULL; dtrace_byfunc = NULL; dtrace_byname = NULL; kmem_cache_destroy(dtrace_state_cache); vmem_destroy(dtrace_minor); vmem_destroy(dtrace_arena); if (dtrace_toxrange != NULL) { kmem_free(dtrace_toxrange, dtrace_toxranges_max * sizeof (dtrace_toxrange_t)); dtrace_toxrange = NULL; dtrace_toxranges = 0; dtrace_toxranges_max = 0; } ddi_remove_minor_node(dtrace_devi, NULL); dtrace_devi = NULL; ddi_soft_state_fini(&dtrace_softstate); ASSERT(dtrace_vtime_references == 0); ASSERT(dtrace_opens == 0); ASSERT(dtrace_retained == NULL); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); /* * We don't destroy the task queue until after we have dropped our * locks (taskq_destroy() may block on running tasks). To prevent * attempting to do work after we have effectively detached but before * the task queue has been destroyed, all tasks dispatched via the * task queue must check that DTrace is still attached before * performing any operation. */ taskq_destroy(dtrace_taskq); dtrace_taskq = NULL; return (DDI_SUCCESS); } #endif #ifdef illumos /*ARGSUSED*/ static int dtrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int error; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: *result = (void *)dtrace_devi; error = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)0; error = DDI_SUCCESS; break; default: error = DDI_FAILURE; } return (error); } #endif #ifdef illumos static struct cb_ops dtrace_cb_ops = { dtrace_open, /* open */ dtrace_close, /* close */ nulldev, /* strategy */ nulldev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ dtrace_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ 0, /* streamtab */ D_NEW | D_MP /* Driver compatibility flag */ }; static struct dev_ops dtrace_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ dtrace_info, /* get_dev_info */ nulldev, /* identify */ nulldev, /* probe */ dtrace_attach, /* attach */ dtrace_detach, /* detach */ nodev, /* reset */ &dtrace_cb_ops, /* driver operations */ NULL, /* bus operations */ nodev /* dev power */ }; static struct modldrv modldrv = { &mod_driverops, /* module type (this is a pseudo driver) */ "Dynamic Tracing", /* name of module */ &dtrace_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *)&modldrv, NULL }; int _init(void) { return (mod_install(&modlinkage)); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { return (mod_remove(&modlinkage)); } #else static d_ioctl_t dtrace_ioctl; static d_ioctl_t dtrace_ioctl_helper; static void dtrace_load(void *); static int dtrace_unload(void); static struct cdev *dtrace_dev; static struct cdev *helper_dev; void dtrace_invop_init(void); void dtrace_invop_uninit(void); static struct cdevsw dtrace_cdevsw = { .d_version = D_VERSION, .d_ioctl = dtrace_ioctl, .d_open = dtrace_open, .d_name = "dtrace", }; static struct cdevsw helper_cdevsw = { .d_version = D_VERSION, .d_ioctl = dtrace_ioctl_helper, .d_name = "helper", }; #include #include #include #include #include #include #include #include #include SYSINIT(dtrace_load, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_load, NULL); SYSUNINIT(dtrace_unload, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_unload, NULL); SYSINIT(dtrace_anon_init, SI_SUB_DTRACE_ANON, SI_ORDER_FIRST, dtrace_anon_init, NULL); DEV_MODULE(dtrace, dtrace_modevent, NULL); MODULE_VERSION(dtrace, 1); MODULE_DEPEND(dtrace, opensolaris, 1, 1, 1); #endif Index: projects/building-blocks/sys/cddl/contrib/opensolaris =================================================================== --- projects/building-blocks/sys/cddl/contrib/opensolaris (revision 278195) +++ projects/building-blocks/sys/cddl/contrib/opensolaris (revision 278196) Property changes on: projects/building-blocks/sys/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /head/sys/cddl/contrib/opensolaris:r278050-278194 Merged /vendor-sys/illumos/dist:r266993,266995 Index: projects/building-blocks/sys/cddl/dev/dtrace/dtrace_load.c =================================================================== --- projects/building-blocks/sys/cddl/dev/dtrace/dtrace_load.c (revision 278195) +++ projects/building-blocks/sys/cddl/dev/dtrace/dtrace_load.c (revision 278196) @@ -1,167 +1,156 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * $FreeBSD$ * */ static void dtrace_ap_start(void *dummy) { int i; mutex_enter(&cpu_lock); /* Setup the rest of the CPUs. */ CPU_FOREACH(i) { if (i == 0) continue; (void) dtrace_cpu_setup(CPU_CONFIG, i); } mutex_exit(&cpu_lock); } SYSINIT(dtrace_ap_start, SI_SUB_SMP, SI_ORDER_ANY, dtrace_ap_start, NULL); static void dtrace_load(void *dummy) { dtrace_provider_id_t id; /* Hook into the trap handler. */ dtrace_trap_func = dtrace_trap; /* Hang our hook for thread switches. */ dtrace_vtime_switch_func = dtrace_vtime_switch; /* Hang our hook for exceptions. */ dtrace_invop_init(); dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri, 0, 0, 0); dtrace_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx); /* Register callbacks for linker file load and unload events. */ dtrace_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, dtrace_kld_load, NULL, EVENTHANDLER_PRI_ANY); dtrace_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try, dtrace_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY); /* * Initialise the mutexes without 'witness' because the dtrace * code is mostly written to wait for memory. To have the * witness code change a malloc() from M_WAITOK to M_NOWAIT * because a lock is held would surely create a panic in a * low memory situation. And that low memory situation might be * the very problem we are trying to trace. */ mutex_init(&dtrace_lock,"dtrace probe state", MUTEX_DEFAULT, NULL); mutex_init(&dtrace_provider_lock,"dtrace provider state", MUTEX_DEFAULT, NULL); mutex_init(&dtrace_meta_lock,"dtrace meta-provider state", MUTEX_DEFAULT, NULL); #ifdef DEBUG mutex_init(&dtrace_errlock,"dtrace error lock", MUTEX_DEFAULT, NULL); #endif mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); mutex_enter(&cpu_lock); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_state_cache = kmem_cache_create("dtrace_state_cache", sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN, NULL, NULL, NULL, NULL, NULL, 0); ASSERT(MUTEX_HELD(&cpu_lock)); dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod), offsetof(dtrace_probe_t, dtpr_nextmod), offsetof(dtrace_probe_t, dtpr_prevmod)); dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func), offsetof(dtrace_probe_t, dtpr_nextfunc), offsetof(dtrace_probe_t, dtpr_prevfunc)); dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name), offsetof(dtrace_probe_t, dtpr_nextname), offsetof(dtrace_probe_t, dtpr_prevname)); if (dtrace_retain_max < 1) { cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; " "setting to 1", dtrace_retain_max); dtrace_retain_max = 1; } /* * Now discover our toxic ranges. */ dtrace_toxic_ranges(dtrace_toxrange_add); /* * Before we register ourselves as a provider to our own framework, * we would like to assert that dtrace_provider is NULL -- but that's * not true if we were loaded as a dependency of a DTrace provider. * Once we've registered, we can assert that dtrace_provider is our * pseudo provider. */ (void) dtrace_register("dtrace", &dtrace_provider_attr, DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id); ASSERT(dtrace_provider != NULL); ASSERT((dtrace_provider_id_t)dtrace_provider == id); dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "BEGIN", 0, NULL); dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "END", 0, NULL); dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t) dtrace_provider, NULL, NULL, "ERROR", 1, NULL); mutex_exit(&cpu_lock); - /* - * If DTrace helper tracing is enabled, we need to allocate the - * trace buffer and initialize the values. - */ - if (dtrace_helptrace_enabled) { - ASSERT(dtrace_helptrace_buffer == NULL); - dtrace_helptrace_buffer = - kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP); - dtrace_helptrace_next = 0; - } - mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); mutex_enter(&cpu_lock); /* Setup the boot CPU */ (void) dtrace_cpu_setup(CPU_CONFIG, 0); mutex_exit(&cpu_lock); dtrace_dev = make_dev(&dtrace_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "dtrace/dtrace"); helper_dev = make_dev(&helper_cdevsw, 0, UID_ROOT, GID_WHEEL, 0660, "dtrace/helper"); return; } Index: projects/building-blocks/sys/cddl/dev/dtrace/dtrace_unload.c =================================================================== --- projects/building-blocks/sys/cddl/dev/dtrace/dtrace_unload.c (revision 278195) +++ projects/building-blocks/sys/cddl/dev/dtrace/dtrace_unload.c (revision 278196) @@ -1,131 +1,126 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * $FreeBSD$ * */ static int dtrace_unload() { dtrace_state_t *state; int error = 0; destroy_dev(dtrace_dev); destroy_dev(helper_dev); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); mutex_enter(&cpu_lock); ASSERT(dtrace_opens == 0); if (dtrace_helpers > 0) { mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (EBUSY); } if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) { mutex_exit(&cpu_lock); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); return (EBUSY); } dtrace_provider = NULL; EVENTHANDLER_DEREGISTER(kld_load, dtrace_kld_load_tag); EVENTHANDLER_DEREGISTER(kld_unload_try, dtrace_kld_unload_try_tag); if ((state = dtrace_anon_grab()) != NULL) { /* * If there were ECBs on this state, the provider should * have not been allowed to detach; assert that there is * none. */ ASSERT(state->dts_necbs == 0); dtrace_state_destroy(state); } bzero(&dtrace_anon, sizeof (dtrace_anon_t)); mutex_exit(&cpu_lock); - if (dtrace_helptrace_enabled) { - kmem_free(dtrace_helptrace_buffer, 0); - dtrace_helptrace_buffer = NULL; - } - if (dtrace_probes != NULL) { kmem_free(dtrace_probes, 0); dtrace_probes = NULL; dtrace_nprobes = 0; } dtrace_hash_destroy(dtrace_bymod); dtrace_hash_destroy(dtrace_byfunc); dtrace_hash_destroy(dtrace_byname); dtrace_bymod = NULL; dtrace_byfunc = NULL; dtrace_byname = NULL; kmem_cache_destroy(dtrace_state_cache); delete_unrhdr(dtrace_arena); if (dtrace_toxrange != NULL) { kmem_free(dtrace_toxrange, 0); dtrace_toxrange = NULL; dtrace_toxranges = 0; dtrace_toxranges_max = 0; } ASSERT(dtrace_vtime_references == 0); ASSERT(dtrace_opens == 0); ASSERT(dtrace_retained == NULL); mutex_exit(&dtrace_lock); mutex_exit(&dtrace_provider_lock); mutex_destroy(&dtrace_meta_lock); mutex_destroy(&dtrace_provider_lock); mutex_destroy(&dtrace_lock); #ifdef DEBUG mutex_destroy(&dtrace_errlock); #endif taskq_destroy(dtrace_taskq); /* Reset our hook for exceptions. */ dtrace_invop_uninit(); /* * Reset our hook for thread switches, but ensure that vtime isn't * active first. */ dtrace_vtime_active = 0; dtrace_vtime_switch_func = NULL; /* Unhook from the trap handler. */ dtrace_trap_func = NULL; return (error); } Index: projects/building-blocks/sys/conf/files.amd64 =================================================================== --- projects/building-blocks/sys/conf/files.amd64 (revision 278195) +++ projects/building-blocks/sys/conf/files.amd64 (revision 278196) @@ -1,582 +1,583 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # # linux32_genassym.o optional compat_linux32 \ dependency "$S/amd64/linux32/linux32_genassym.c" \ compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "linux32_genassym.o" # linux32_assym.h optional compat_linux32 \ dependency "$S/kern/genassym.sh linux32_genassym.o" \ compile-with "sh $S/kern/genassym.sh linux32_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "linux32_assym.h" # ia32_genassym.o standard \ dependency "$S/compat/ia32/ia32_genassym.c" \ compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "ia32_genassym.o" # ia32_assym.h standard \ dependency "$S/kern/genassym.sh ia32_genassym.o" \ compile-with "env NM='${NM}' sh $S/kern/genassym.sh ia32_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "ia32_assym.h" # font.h optional sc_dflt_font \ compile-with "uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'static u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'static u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'static u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h" \ no-obj no-implicit-rule before-depend \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # hpt27xx_lib.o optional hpt27xx \ dependency "$S/dev/hpt27xx/amd64-elf.hpt27xx_lib.o.uu" \ compile-with "uudecode < $S/dev/hpt27xx/amd64-elf.hpt27xx_lib.o.uu" \ no-implicit-rule # hptmvraid.o optional hptmv \ dependency "$S/dev/hptmv/amd64-elf.raid.o.uu" \ compile-with "uudecode < $S/dev/hptmv/amd64-elf.raid.o.uu" \ no-implicit-rule # hptnr_lib.o optional hptnr \ dependency "$S/dev/hptnr/amd64-elf.hptnr_lib.o.uu" \ compile-with "uudecode < $S/dev/hptnr/amd64-elf.hptnr_lib.o.uu" \ no-implicit-rule # hptrr_lib.o optional hptrr \ dependency "$S/dev/hptrr/amd64-elf.hptrr_lib.o.uu" \ compile-with "uudecode < $S/dev/hptrr/amd64-elf.hptrr_lib.o.uu" \ no-implicit-rule # amd64/acpica/acpi_machdep.c optional acpi acpi_wakecode.o optional acpi \ dependency "$S/amd64/acpica/acpi_wakecode.S assym.s" \ compile-with "${NORMAL_S}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.o" acpi_wakecode.bin optional acpi \ dependency "acpi_wakecode.o" \ compile-with "${OBJCOPY} -S -O binary acpi_wakecode.o ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.bin" acpi_wakecode.h optional acpi \ dependency "acpi_wakecode.bin" \ compile-with "file2c -sx 'static char wakecode[] = {' '};' < acpi_wakecode.bin > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.h" acpi_wakedata.h optional acpi \ dependency "acpi_wakecode.o" \ compile-with '${NM} -n --defined-only acpi_wakecode.o | while read offset dummy what; do echo "#define $${what} 0x$${offset}"; done > ${.TARGET}' \ no-obj no-implicit-rule before-depend \ clean "acpi_wakedata.h" # amd64/amd64/amd64_mem.c optional mem #amd64/amd64/apic_vector.S standard amd64/amd64/atomic.c standard amd64/amd64/autoconf.c standard amd64/amd64/bios.c standard amd64/amd64/bpf_jit_machdep.c optional bpf_jitter amd64/amd64/cpu_switch.S standard amd64/amd64/db_disasm.c optional ddb amd64/amd64/db_interface.c optional ddb amd64/amd64/db_trace.c optional ddb amd64/amd64/elf_machdep.c standard amd64/amd64/exception.S standard amd64/amd64/fpu.c standard amd64/amd64/gdb_machdep.c optional gdb amd64/amd64/in_cksum.c optional inet | inet6 amd64/amd64/initcpu.c standard amd64/amd64/io.c optional io amd64/amd64/locore.S standard no-obj amd64/amd64/xen-locore.S optional xenhvm amd64/amd64/machdep.c standard amd64/amd64/mem.c optional mem amd64/amd64/minidump_machdep.c standard amd64/amd64/mp_machdep.c optional smp amd64/amd64/mp_watchdog.c optional mp_watchdog smp amd64/amd64/mpboot.S optional smp amd64/amd64/pmap.c standard amd64/amd64/prof_machdep.c optional profiling-routine amd64/amd64/ptrace_machdep.c standard amd64/amd64/sigtramp.S standard amd64/amd64/stack_machdep.c optional ddb | stack amd64/amd64/support.S standard amd64/amd64/sys_machdep.c standard amd64/amd64/trap.c standard amd64/amd64/uio_machdep.c standard amd64/amd64/uma_machdep.c standard amd64/amd64/vm_machdep.c standard amd64/pci/pci_cfgreg.c optional pci cddl/contrib/opensolaris/common/atomic/amd64/opensolaris_atomic.S optional zfs compile-with "${ZFS_S}" crypto/aesni/aeskeys_amd64.S optional aesni crypto/aesni/aesni.c optional aesni aesni_ghash.o optional aesni \ dependency "$S/crypto/aesni/aesni_ghash.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_ghash.o" aesni_wrap.o optional aesni \ dependency "$S/crypto/aesni/aesni_wrap.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_wrap.o" crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb crypto/via/padlock.c optional padlock crypto/via/padlock_cipher.c optional padlock crypto/via/padlock_hash.c optional padlock dev/acpica/acpi_if.m standard dev/acpi_support/acpi_wmi_if.m standard dev/agp/agp_amd64.c optional agp dev/agp/agp_i810.c optional agp dev/agp/agp_via.c optional agp dev/amdsbwd/amdsbwd.c optional amdsbwd dev/amdtemp/amdtemp.c optional amdtemp dev/arcmsr/arcmsr.c optional arcmsr pci dev/asmc/asmc.c optional asmc isa dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc dev/atkbdc/atkbdc_isa.c optional atkbdc isa dev/atkbdc/atkbdc_subr.c optional atkbdc dev/atkbdc/psm.c optional psm atkbdc dev/bxe/bxe.c optional bxe pci dev/bxe/bxe_stats.c optional bxe pci dev/bxe/bxe_debug.c optional bxe pci dev/bxe/ecore_sp.c optional bxe pci dev/bxe/bxe_elink.c optional bxe pci dev/bxe/57710_init_values.c optional bxe pci dev/bxe/57711_init_values.c optional bxe pci dev/bxe/57712_init_values.c optional bxe pci dev/coretemp/coretemp.c optional coretemp dev/cpuctl/cpuctl.c optional cpuctl dev/dpms/dpms.c optional dpms # There are no systems with isa slots, so all ed isa entries should go.. dev/ed/if_ed_3c503.c optional ed isa ed_3c503 dev/ed/if_ed_isa.c optional ed isa dev/ed/if_ed_wd80x3.c optional ed isa dev/ed/if_ed_hpp.c optional ed isa ed_hpp dev/ed/if_ed_sic.c optional ed isa ed_sic dev/fb/fb.c optional fb | vga dev/fb/s3_pci.c optional s3pci dev/fb/vesa.c optional vga vesa dev/fb/vga.c optional vga dev/ichwd/ichwd.c optional ichwd dev/if_ndis/if_ndis.c optional ndis dev/if_ndis/if_ndis_pccard.c optional ndis pccard dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci dev/if_ndis/if_ndis_usb.c optional ndis usb dev/io/iodev.c optional io dev/ipmi/ipmi.c optional ipmi dev/ipmi/ipmi_acpi.c optional ipmi acpi dev/ipmi/ipmi_isa.c optional ipmi isa dev/ipmi/ipmi_kcs.c optional ipmi dev/ipmi/ipmi_smic.c optional ipmi dev/ipmi/ipmi_smbus.c optional ipmi smbus dev/ipmi/ipmi_smbios.c optional ipmi dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/ipmi/ipmi_linux.c optional ipmi compat_linux32 dev/ixl/if_ixl.c optional ixl pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/if_ixlv.c optional ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/ixlvc.c optional ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/ixl_txrx.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_osdep.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_lan_hmc.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_hmc.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_common.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_nvm.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/ixl/i40e_adminq.c optional ixl pci | ixlv pci \ compile-with "${NORMAL_C} -I$S/dev/ixl" dev/fdc/fdc.c optional fdc dev/fdc/fdc_acpi.c optional fdc dev/fdc/fdc_isa.c optional fdc isa dev/fdc/fdc_pccard.c optional fdc pccard dev/fdt/fdt_x86.c optional fdt dev/hpt27xx/hpt27xx_os_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_osm_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_config.c optional hpt27xx dev/hptmv/entry.c optional hptmv dev/hptmv/mv.c optional hptmv dev/hptmv/gui_lib.c optional hptmv dev/hptmv/hptproc.c optional hptmv dev/hptmv/ioctl.c optional hptmv dev/hptnr/hptnr_os_bsd.c optional hptnr dev/hptnr/hptnr_osm_bsd.c optional hptnr dev/hptnr/hptnr_config.c optional hptnr dev/hptrr/hptrr_os_bsd.c optional hptrr dev/hptrr/hptrr_osm_bsd.c optional hptrr dev/hptrr/hptrr_config.c optional hptrr dev/hwpmc/hwpmc_amd.c optional hwpmc dev/hwpmc/hwpmc_intel.c optional hwpmc dev/hwpmc/hwpmc_core.c optional hwpmc dev/hwpmc/hwpmc_uncore.c optional hwpmc dev/hwpmc/hwpmc_piv.c optional hwpmc dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc dev/hyperv/netvsc/hv_net_vsc.c optional hyperv dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c optional hyperv dev/hyperv/netvsc/hv_rndis_filter.c optional hyperv dev/hyperv/stordisengage/hv_ata_pci_disengage.c optional hyperv dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv dev/hyperv/utilities/hv_kvp.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_connection.c optional hyperv dev/hyperv/vmbus/hv_hv.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c optional hyperv dev/kbd/kbd.c optional atkbd | sc | ukbd | vt dev/nfe/if_nfe.c optional nfe pci dev/ntb/if_ntb/if_ntb.c optional if_ntb dev/ntb/ntb_hw/ntb_hw.c optional if_ntb ntb_hw dev/nvd/nvd.c optional nvd nvme dev/nvme/nvme.c optional nvme dev/nvme/nvme_ctrlr.c optional nvme dev/nvme/nvme_ctrlr_cmd.c optional nvme dev/nvme/nvme_ns.c optional nvme dev/nvme/nvme_ns_cmd.c optional nvme dev/nvme/nvme_qpair.c optional nvme dev/nvme/nvme_sysctl.c optional nvme dev/nvme/nvme_test.c optional nvme dev/nvme/nvme_util.c optional nvme dev/nvram/nvram.c optional nvram isa dev/random/ivy.c optional rdrand_rng dev/random/nehemiah.c optional padlock_rng dev/qlxge/qls_dbg.c optional qlxge pci dev/qlxge/qls_dump.c optional qlxge pci dev/qlxge/qls_hw.c optional qlxge pci dev/qlxge/qls_ioctl.c optional qlxge pci dev/qlxge/qls_isr.c optional qlxge pci dev/qlxge/qls_os.c optional qlxge pci dev/qlxgb/qla_dbg.c optional qlxgb pci dev/qlxgb/qla_hw.c optional qlxgb pci dev/qlxgb/qla_ioctl.c optional qlxgb pci dev/qlxgb/qla_isr.c optional qlxgb pci dev/qlxgb/qla_misc.c optional qlxgb pci dev/qlxgb/qla_os.c optional qlxgb pci dev/qlxgbe/ql_dbg.c optional qlxgbe pci dev/qlxgbe/ql_hw.c optional qlxgbe pci dev/qlxgbe/ql_ioctl.c optional qlxgbe pci dev/qlxgbe/ql_isr.c optional qlxgbe pci dev/qlxgbe/ql_misc.c optional qlxgbe pci dev/qlxgbe/ql_os.c optional qlxgbe pci dev/qlxgbe/ql_reset.c optional qlxgbe pci dev/sfxge/common/efx_bootcfg.c optional sfxge inet pci dev/sfxge/common/efx_ev.c optional sfxge inet pci dev/sfxge/common/efx_filter.c optional sfxge inet pci dev/sfxge/common/efx_intr.c optional sfxge inet pci dev/sfxge/common/efx_mac.c optional sfxge inet pci dev/sfxge/common/efx_mcdi.c optional sfxge inet pci dev/sfxge/common/efx_mon.c optional sfxge inet pci dev/sfxge/common/efx_nic.c optional sfxge inet pci dev/sfxge/common/efx_nvram.c optional sfxge inet pci dev/sfxge/common/efx_phy.c optional sfxge inet pci dev/sfxge/common/efx_port.c optional sfxge inet pci dev/sfxge/common/efx_rx.c optional sfxge inet pci dev/sfxge/common/efx_sram.c optional sfxge inet pci dev/sfxge/common/efx_tx.c optional sfxge inet pci dev/sfxge/common/efx_vpd.c optional sfxge inet pci dev/sfxge/common/efx_wol.c optional sfxge inet pci dev/sfxge/common/siena_mac.c optional sfxge inet pci dev/sfxge/common/siena_mon.c optional sfxge inet pci dev/sfxge/common/siena_nic.c optional sfxge inet pci dev/sfxge/common/siena_nvram.c optional sfxge inet pci dev/sfxge/common/siena_phy.c optional sfxge inet pci dev/sfxge/common/siena_sram.c optional sfxge inet pci dev/sfxge/common/siena_vpd.c optional sfxge inet pci dev/sfxge/sfxge.c optional sfxge inet pci dev/sfxge/sfxge_dma.c optional sfxge inet pci dev/sfxge/sfxge_ev.c optional sfxge inet pci dev/sfxge/sfxge_intr.c optional sfxge inet pci dev/sfxge/sfxge_mcdi.c optional sfxge inet pci dev/sfxge/sfxge_port.c optional sfxge inet pci dev/sfxge/sfxge_rx.c optional sfxge inet pci dev/sfxge/sfxge_tx.c optional sfxge inet pci dev/sio/sio.c optional sio dev/sio/sio_isa.c optional sio isa dev/sio/sio_pccard.c optional sio pccard dev/sio/sio_pci.c optional sio pci dev/sio/sio_puc.c optional sio puc dev/speaker/spkr.c optional speaker dev/syscons/apm/apm_saver.c optional apm_saver apm dev/syscons/scterm-teken.c optional sc dev/syscons/scvesactl.c optional sc vga vesa dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvtb.c optional sc dev/tpm/tpm.c optional tpm dev/tpm/tpm_acpi.c optional tpm acpi dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_x86.c optional uart dev/viawd/viawd.c optional viawd dev/vmware/vmxnet3/if_vmx.c optional vmx dev/wbwd/wbwd.c optional wbwd dev/wpi/if_wpi.c optional wpi dev/xen/pci/xen_acpi_pci.c optional xenhvm dev/xen/pci/xen_pci.c optional xenhvm dev/isci/isci.c optional isci dev/isci/isci_controller.c optional isci dev/isci/isci_domain.c optional isci dev/isci/isci_interrupt.c optional isci dev/isci/isci_io_request.c optional isci dev/isci/isci_logger.c optional isci dev/isci/isci_oem_parameters.c optional isci dev/isci/isci_remote_device.c optional isci dev/isci/isci_sysctl.c optional isci dev/isci/isci_task_request.c optional isci dev/isci/isci_timer.c optional isci dev/isci/scil/sati.c optional isci dev/isci/scil/sati_abort_task_set.c optional isci dev/isci/scil/sati_atapi.c optional isci dev/isci/scil/sati_device.c optional isci dev/isci/scil/sati_inquiry.c optional isci dev/isci/scil/sati_log_sense.c optional isci dev/isci/scil/sati_lun_reset.c optional isci dev/isci/scil/sati_mode_pages.c optional isci dev/isci/scil/sati_mode_select.c optional isci dev/isci/scil/sati_mode_sense.c optional isci dev/isci/scil/sati_mode_sense_10.c optional isci dev/isci/scil/sati_mode_sense_6.c optional isci dev/isci/scil/sati_move.c optional isci dev/isci/scil/sati_passthrough.c optional isci dev/isci/scil/sati_read.c optional isci dev/isci/scil/sati_read_buffer.c optional isci dev/isci/scil/sati_read_capacity.c optional isci dev/isci/scil/sati_reassign_blocks.c optional isci dev/isci/scil/sati_report_luns.c optional isci dev/isci/scil/sati_request_sense.c optional isci dev/isci/scil/sati_start_stop_unit.c optional isci dev/isci/scil/sati_synchronize_cache.c optional isci dev/isci/scil/sati_test_unit_ready.c optional isci dev/isci/scil/sati_unmap.c optional isci dev/isci/scil/sati_util.c optional isci dev/isci/scil/sati_verify.c optional isci dev/isci/scil/sati_write.c optional isci dev/isci/scil/sati_write_and_verify.c optional isci dev/isci/scil/sati_write_buffer.c optional isci dev/isci/scil/sati_write_long.c optional isci dev/isci/scil/sci_abstract_list.c optional isci dev/isci/scil/sci_base_controller.c optional isci dev/isci/scil/sci_base_domain.c optional isci dev/isci/scil/sci_base_iterator.c optional isci dev/isci/scil/sci_base_library.c optional isci dev/isci/scil/sci_base_logger.c optional isci dev/isci/scil/sci_base_memory_descriptor_list.c optional isci dev/isci/scil/sci_base_memory_descriptor_list_decorator.c optional isci dev/isci/scil/sci_base_object.c optional isci dev/isci/scil/sci_base_observer.c optional isci dev/isci/scil/sci_base_phy.c optional isci dev/isci/scil/sci_base_port.c optional isci dev/isci/scil/sci_base_remote_device.c optional isci dev/isci/scil/sci_base_request.c optional isci dev/isci/scil/sci_base_state_machine.c optional isci dev/isci/scil/sci_base_state_machine_logger.c optional isci dev/isci/scil/sci_base_state_machine_observer.c optional isci dev/isci/scil/sci_base_subject.c optional isci dev/isci/scil/sci_util.c optional isci dev/isci/scil/scic_sds_controller.c optional isci dev/isci/scil/scic_sds_library.c optional isci dev/isci/scil/scic_sds_pci.c optional isci dev/isci/scil/scic_sds_phy.c optional isci dev/isci/scil/scic_sds_port.c optional isci dev/isci/scil/scic_sds_port_configuration_agent.c optional isci dev/isci/scil/scic_sds_remote_device.c optional isci dev/isci/scil/scic_sds_remote_node_context.c optional isci dev/isci/scil/scic_sds_remote_node_table.c optional isci dev/isci/scil/scic_sds_request.c optional isci dev/isci/scil/scic_sds_sgpio.c optional isci dev/isci/scil/scic_sds_smp_remote_device.c optional isci dev/isci/scil/scic_sds_smp_request.c optional isci dev/isci/scil/scic_sds_ssp_request.c optional isci dev/isci/scil/scic_sds_stp_packet_request.c optional isci dev/isci/scil/scic_sds_stp_remote_device.c optional isci dev/isci/scil/scic_sds_stp_request.c optional isci dev/isci/scil/scic_sds_unsolicited_frame_control.c optional isci dev/isci/scil/scif_sas_controller.c optional isci dev/isci/scil/scif_sas_controller_state_handlers.c optional isci dev/isci/scil/scif_sas_controller_states.c optional isci dev/isci/scil/scif_sas_domain.c optional isci dev/isci/scil/scif_sas_domain_state_handlers.c optional isci dev/isci/scil/scif_sas_domain_states.c optional isci dev/isci/scil/scif_sas_high_priority_request_queue.c optional isci dev/isci/scil/scif_sas_internal_io_request.c optional isci dev/isci/scil/scif_sas_io_request.c optional isci dev/isci/scil/scif_sas_io_request_state_handlers.c optional isci dev/isci/scil/scif_sas_io_request_states.c optional isci dev/isci/scil/scif_sas_library.c optional isci dev/isci/scil/scif_sas_remote_device.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substates.c optional isci dev/isci/scil/scif_sas_remote_device_starting_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_starting_substates.c optional isci dev/isci/scil/scif_sas_remote_device_state_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_states.c optional isci dev/isci/scil/scif_sas_request.c optional isci dev/isci/scil/scif_sas_smp_activity_clear_affiliation.c optional isci dev/isci/scil/scif_sas_smp_io_request.c optional isci dev/isci/scil/scif_sas_smp_phy.c optional isci dev/isci/scil/scif_sas_smp_remote_device.c optional isci dev/isci/scil/scif_sas_stp_io_request.c optional isci dev/isci/scil/scif_sas_stp_remote_device.c optional isci dev/isci/scil/scif_sas_stp_task_request.c optional isci dev/isci/scil/scif_sas_task_request.c optional isci dev/isci/scil/scif_sas_task_request_state_handlers.c optional isci dev/isci/scil/scif_sas_task_request_states.c optional isci dev/isci/scil/scif_sas_timer.c optional isci isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/kern_clocksource.c standard kern/link_elf_obj.c standard # # IA32 binary support # #amd64/ia32/ia32_exception.S optional compat_freebsd32 amd64/ia32/ia32_reg.c optional compat_freebsd32 amd64/ia32/ia32_signal.c optional compat_freebsd32 amd64/ia32/ia32_sigtramp.S optional compat_freebsd32 amd64/ia32/ia32_syscall.c optional compat_freebsd32 amd64/ia32/ia32_misc.c optional compat_freebsd32 compat/ia32/ia32_sysvec.c optional compat_freebsd32 compat/linprocfs/linprocfs.c optional linprocfs compat/linsysfs/linsysfs.c optional linsysfs # # Linux/i386 binary support # amd64/linux32/linux32_dummy.c optional compat_linux32 amd64/linux32/linux32_locore.s optional compat_linux32 \ dependency "linux32_assym.h" amd64/linux32/linux32_machdep.c optional compat_linux32 amd64/linux32/linux32_support.s optional compat_linux32 \ dependency "linux32_assym.h" amd64/linux32/linux32_sysent.c optional compat_linux32 amd64/linux32/linux32_sysvec.c optional compat_linux32 compat/linux/linux_emul.c optional compat_linux32 compat/linux/linux_file.c optional compat_linux32 compat/linux/linux_fork.c optional compat_linux32 compat/linux/linux_futex.c optional compat_linux32 compat/linux/linux_getcwd.c optional compat_linux32 compat/linux/linux_ioctl.c optional compat_linux32 compat/linux/linux_ipc.c optional compat_linux32 compat/linux/linux_mib.c optional compat_linux32 compat/linux/linux_misc.c optional compat_linux32 compat/linux/linux_signal.c optional compat_linux32 compat/linux/linux_socket.c optional compat_linux32 compat/linux/linux_stats.c optional compat_linux32 compat/linux/linux_sysctl.c optional compat_linux32 compat/linux/linux_time.c optional compat_linux32 compat/linux/linux_timer.c optional compat_linux32 compat/linux/linux_uid16.c optional compat_linux32 compat/linux/linux_util.c optional compat_linux32 dev/amr/amr_linux.c optional compat_linux32 amr dev/mfi/mfi_linux.c optional compat_linux32 mfi # # Windows NDIS driver support # compat/ndis/kern_ndis.c optional ndisapi pci compat/ndis/kern_windrv.c optional ndisapi pci compat/ndis/subr_hal.c optional ndisapi pci compat/ndis/subr_ndis.c optional ndisapi pci compat/ndis/subr_ntoskrnl.c optional ndisapi pci compat/ndis/subr_pe.c optional ndisapi pci compat/ndis/subr_usbd.c optional ndisapi pci compat/ndis/winx64_wrap.S optional ndisapi pci # libkern/memmove.c standard libkern/memset.c standard # # x86 real mode BIOS emulator, required by atkbdc/dpms/vesa # compat/x86bios/x86bios.c optional x86bios | atkbd | dpms | vesa contrib/x86emu/x86emu.c optional x86bios | atkbd | dpms | vesa # # bvm console # dev/bvm/bvm_console.c optional bvmconsole dev/bvm/bvm_dbg.c optional bvmdebug # # x86 shared code between IA32, AMD64 and PC98 architectures # x86/acpica/OsdEnvironment.c optional acpi x86/acpica/acpi_apm.c optional acpi x86/acpica/acpi_wakeup.c optional acpi x86/acpica/madt.c optional acpi x86/acpica/srat.c optional acpi x86/bios/smbios.c optional smbios x86/bios/vpd.c optional vpd x86/cpufreq/powernow.c optional cpufreq x86/cpufreq/est.c optional cpufreq x86/cpufreq/hwpstate.c optional cpufreq x86/cpufreq/p4tcc.c optional cpufreq x86/iommu/busdma_dmar.c optional acpi acpi_dmar pci x86/iommu/intel_ctx.c optional acpi acpi_dmar pci x86/iommu/intel_drv.c optional acpi acpi_dmar pci x86/iommu/intel_fault.c optional acpi acpi_dmar pci x86/iommu/intel_gas.c optional acpi acpi_dmar pci x86/iommu/intel_idpgtbl.c optional acpi acpi_dmar pci x86/iommu/intel_qi.c optional acpi acpi_dmar pci x86/iommu/intel_quirks.c optional acpi acpi_dmar pci x86/iommu/intel_utils.c optional acpi acpi_dmar pci x86/isa/atpic.c optional atpic isa x86/isa/atrtc.c standard x86/isa/clock.c standard x86/isa/elcr.c optional atpic isa | mptable x86/isa/isa.c standard x86/isa/isa_dma.c standard x86/isa/nmi.c standard x86/isa/orm.c optional isa x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard x86/x86/intr_machdep.c standard x86/x86/io_apic.c standard x86/x86/legacy.c standard x86/x86/local_apic.c standard x86/x86/mca.c standard x86/x86/mptable.c optional mptable x86/x86/mptable_pci.c optional mptable pci x86/x86/msi.c optional pci x86/x86/nexus.c standard +x86/x86/pvclock.c standard x86/x86/tsc.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_intr.c optional xen | xenhvm x86/xen/pv.c optional xenhvm x86/xen/pvcpu_enum.c optional xenhvm x86/xen/xen_apic.c optional xenhvm x86/xen/xenpv.c optional xenhvm x86/xen/xen_nexus.c optional xenhvm x86/xen/xen_msi.c optional xenhvm x86/xen/xen_pci_bus.c optional xenhvm Index: projects/building-blocks/sys/conf/files.i386 =================================================================== --- projects/building-blocks/sys/conf/files.i386 (revision 278195) +++ projects/building-blocks/sys/conf/files.i386 (revision 278196) @@ -1,597 +1,598 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # linux_genassym.o optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c" \ compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "linux_genassym.o" # linux_assym.h optional compat_linux \ dependency "$S/kern/genassym.sh linux_genassym.o" \ compile-with "sh $S/kern/genassym.sh linux_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # svr4_genassym.o optional compat_svr4 \ dependency "$S/i386/svr4/svr4_genassym.c" \ compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "svr4_genassym.o" # svr4_assym.h optional compat_svr4 \ dependency "$S/kern/genassym.sh svr4_genassym.o" \ compile-with "sh $S/kern/genassym.sh svr4_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "svr4_assym.h" # font.h optional sc_dflt_font \ compile-with "uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'static u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'static u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'static u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h" \ no-obj no-implicit-rule before-depend \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # hpt27xx_lib.o optional hpt27xx \ dependency "$S/dev/hpt27xx/i386-elf.hpt27xx_lib.o.uu" \ compile-with "uudecode < $S/dev/hpt27xx/i386-elf.hpt27xx_lib.o.uu" \ no-implicit-rule # hptmvraid.o optional hptmv \ dependency "$S/dev/hptmv/i386-elf.raid.o.uu" \ compile-with "uudecode < $S/dev/hptmv/i386-elf.raid.o.uu" \ no-implicit-rule # hptnr_lib.o optional hptnr \ dependency "$S/dev/hptnr/i386-elf.hptnr_lib.o.uu" \ compile-with "uudecode < $S/dev/hptnr/i386-elf.hptnr_lib.o.uu" \ no-implicit-rule # hptrr_lib.o optional hptrr \ dependency "$S/dev/hptrr/i386-elf.hptrr_lib.o.uu" \ compile-with "uudecode < $S/dev/hptrr/i386-elf.hptrr_lib.o.uu" \ no-implicit-rule # cddl/contrib/opensolaris/common/atomic/i386/opensolaris_atomic.S optional zfs compile-with "${ZFS_S}" compat/linprocfs/linprocfs.c optional linprocfs compat/linsysfs/linsysfs.c optional linsysfs compat/linux/linux_emul.c optional compat_linux compat/linux/linux_file.c optional compat_linux compat/linux/linux_fork.c optional compat_linux compat/linux/linux_futex.c optional compat_linux compat/linux/linux_getcwd.c optional compat_linux compat/linux/linux_ioctl.c optional compat_linux compat/linux/linux_ipc.c optional compat_linux compat/linux/linux_mib.c optional compat_linux compat/linux/linux_misc.c optional compat_linux compat/linux/linux_signal.c optional compat_linux compat/linux/linux_socket.c optional compat_linux compat/linux/linux_stats.c optional compat_linux compat/linux/linux_sysctl.c optional compat_linux compat/linux/linux_time.c optional compat_linux compat/linux/linux_timer.c optional compat_linux compat/linux/linux_uid16.c optional compat_linux compat/linux/linux_util.c optional compat_linux compat/ndis/kern_ndis.c optional ndisapi pci compat/ndis/kern_windrv.c optional ndisapi pci compat/ndis/subr_hal.c optional ndisapi pci compat/ndis/subr_ndis.c optional ndisapi pci compat/ndis/subr_ntoskrnl.c optional ndisapi pci compat/ndis/subr_pe.c optional ndisapi pci compat/ndis/subr_usbd.c optional ndisapi pci compat/ndis/winx32_wrap.S optional ndisapi pci compat/svr4/imgact_svr4.c optional compat_svr4 compat/svr4/svr4_fcntl.c optional compat_svr4 compat/svr4/svr4_filio.c optional compat_svr4 compat/svr4/svr4_ioctl.c optional compat_svr4 compat/svr4/svr4_ipc.c optional compat_svr4 compat/svr4/svr4_misc.c optional compat_svr4 compat/svr4/svr4_resource.c optional compat_svr4 compat/svr4/svr4_signal.c optional compat_svr4 compat/svr4/svr4_socket.c optional compat_svr4 compat/svr4/svr4_sockio.c optional compat_svr4 compat/svr4/svr4_stat.c optional compat_svr4 compat/svr4/svr4_stream.c optional compat_svr4 compat/svr4/svr4_syscallnames.c optional compat_svr4 compat/svr4/svr4_sysent.c optional compat_svr4 compat/svr4/svr4_sysvec.c optional compat_svr4 compat/svr4/svr4_termios.c optional compat_svr4 bf_enc.o optional crypto | ipsec \ dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \ compile-with "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}" \ no-implicit-rule crypto/aesni/aeskeys_i386.S optional aesni crypto/aesni/aesni.c optional aesni aesni_ghash.o optional aesni \ dependency "$S/crypto/aesni/aesni_ghash.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_ghash.o" aesni_wrap.o optional aesni \ dependency "$S/crypto/aesni/aesni_wrap.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \ no-implicit-rule \ clean "aesni_wrap.o" crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb crypto/via/padlock.c optional padlock crypto/via/padlock_cipher.c optional padlock crypto/via/padlock_hash.c optional padlock dev/advansys/adv_isa.c optional adv isa dev/agp/agp_ali.c optional agp dev/agp/agp_amd.c optional agp dev/agp/agp_amd64.c optional agp dev/agp/agp_ati.c optional agp dev/agp/agp_i810.c optional agp dev/agp/agp_intel.c optional agp dev/agp/agp_nvidia.c optional agp dev/agp/agp_sis.c optional agp dev/agp/agp_via.c optional agp dev/aic/aic_isa.c optional aic isa dev/amdsbwd/amdsbwd.c optional amdsbwd dev/amdtemp/amdtemp.c optional amdtemp dev/arcmsr/arcmsr.c optional arcmsr pci dev/asmc/asmc.c optional asmc isa dev/atkbdc/atkbd.c optional atkbd atkbdc dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc dev/atkbdc/atkbdc.c optional atkbdc dev/atkbdc/atkbdc_isa.c optional atkbdc isa dev/atkbdc/atkbdc_subr.c optional atkbdc dev/atkbdc/psm.c optional psm atkbdc dev/bxe/bxe.c optional bxe pci dev/bxe/bxe_stats.c optional bxe pci dev/bxe/bxe_debug.c optional bxe pci dev/bxe/ecore_sp.c optional bxe pci dev/bxe/bxe_elink.c optional bxe pci dev/bxe/57710_init_values.c optional bxe pci dev/bxe/57711_init_values.c optional bxe pci dev/bxe/57712_init_values.c optional bxe pci dev/ce/ceddk.c optional ce dev/ce/if_ce.c optional ce dev/ce/tau32-ddk.c optional ce \ compile-with "${NORMAL_C} ${NO_WCONSTANT_CONVERSION}" dev/cm/if_cm_isa.c optional cm isa dev/coretemp/coretemp.c optional coretemp dev/cp/cpddk.c optional cp dev/cp/if_cp.c optional cp dev/cpuctl/cpuctl.c optional cpuctl dev/ctau/ctau.c optional ctau dev/ctau/ctddk.c optional ctau dev/ctau/if_ct.c optional ctau dev/cx/csigma.c optional cx dev/cx/cxddk.c optional cx dev/cx/if_cx.c optional cx dev/dpms/dpms.c optional dpms dev/ed/if_ed_3c503.c optional ed isa ed_3c503 dev/ed/if_ed_isa.c optional ed isa dev/ed/if_ed_wd80x3.c optional ed isa dev/ed/if_ed_hpp.c optional ed isa ed_hpp dev/ed/if_ed_sic.c optional ed isa ed_sic dev/fb/fb.c optional fb | vga dev/fb/s3_pci.c optional s3pci dev/fb/vesa.c optional vga vesa dev/fb/vga.c optional vga dev/fdc/fdc.c optional fdc dev/fdc/fdc_acpi.c optional fdc dev/fdc/fdc_isa.c optional fdc isa dev/fdc/fdc_pccard.c optional fdc pccard dev/fdt/fdt_x86.c optional fdt dev/fe/if_fe_isa.c optional fe isa dev/glxiic/glxiic.c optional glxiic dev/glxsb/glxsb.c optional glxsb dev/glxsb/glxsb_hash.c optional glxsb dev/hpt27xx/hpt27xx_os_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_osm_bsd.c optional hpt27xx dev/hpt27xx/hpt27xx_config.c optional hpt27xx dev/hptmv/entry.c optional hptmv dev/hptmv/mv.c optional hptmv dev/hptmv/gui_lib.c optional hptmv dev/hptmv/hptproc.c optional hptmv dev/hptmv/ioctl.c optional hptmv dev/hptnr/hptnr_os_bsd.c optional hptnr dev/hptnr/hptnr_osm_bsd.c optional hptnr dev/hptnr/hptnr_config.c optional hptnr dev/hptrr/hptrr_os_bsd.c optional hptrr dev/hptrr/hptrr_osm_bsd.c optional hptrr dev/hptrr/hptrr_config.c optional hptrr dev/hwpmc/hwpmc_amd.c optional hwpmc dev/hwpmc/hwpmc_intel.c optional hwpmc dev/hwpmc/hwpmc_core.c optional hwpmc dev/hwpmc/hwpmc_uncore.c optional hwpmc dev/hwpmc/hwpmc_pentium.c optional hwpmc dev/hwpmc/hwpmc_piv.c optional hwpmc dev/hwpmc/hwpmc_ppro.c optional hwpmc dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc dev/hyperv/netvsc/hv_net_vsc.c optional hyperv dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c optional hyperv dev/hyperv/netvsc/hv_rndis_filter.c optional hyperv dev/hyperv/stordisengage/hv_ata_pci_disengage.c optional hyperv dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c optional hyperv dev/hyperv/utilities/hv_kvp.c optional hyperv dev/hyperv/utilities/hv_util.c optional hyperv dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_connection.c optional hyperv dev/hyperv/vmbus/hv_hv.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c optional hyperv dev/ichwd/ichwd.c optional ichwd dev/if_ndis/if_ndis.c optional ndis dev/if_ndis/if_ndis_pccard.c optional ndis pccard dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci dev/if_ndis/if_ndis_usb.c optional ndis usb dev/io/iodev.c optional io dev/ipmi/ipmi.c optional ipmi dev/ipmi/ipmi_acpi.c optional ipmi acpi dev/ipmi/ipmi_isa.c optional ipmi isa dev/ipmi/ipmi_kcs.c optional ipmi dev/ipmi/ipmi_smic.c optional ipmi dev/ipmi/ipmi_smbus.c optional ipmi smbus dev/ipmi/ipmi_smbios.c optional ipmi dev/ipmi/ipmi_ssif.c optional ipmi smbus dev/ipmi/ipmi_pci.c optional ipmi pci dev/ipmi/ipmi_linux.c optional ipmi compat_linux dev/kbd/kbd.c optional atkbd | sc | ukbd | vt dev/le/if_le_isa.c optional le isa dev/mse/mse.c optional mse dev/mse/mse_isa.c optional mse isa dev/nfe/if_nfe.c optional nfe pci dev/nvd/nvd.c optional nvd nvme dev/nvme/nvme.c optional nvme dev/nvme/nvme_ctrlr.c optional nvme dev/nvme/nvme_ctrlr_cmd.c optional nvme dev/nvme/nvme_ns.c optional nvme dev/nvme/nvme_ns_cmd.c optional nvme dev/nvme/nvme_qpair.c optional nvme dev/nvme/nvme_sysctl.c optional nvme dev/nvme/nvme_test.c optional nvme dev/nvme/nvme_util.c optional nvme dev/nvram/nvram.c optional nvram isa dev/pcf/pcf_isa.c optional pcf dev/random/ivy.c optional rdrand_rng dev/random/nehemiah.c optional padlock_rng dev/sbni/if_sbni.c optional sbni dev/sbni/if_sbni_isa.c optional sbni isa dev/sbni/if_sbni_pci.c optional sbni pci dev/sio/sio.c optional sio dev/sio/sio_isa.c optional sio isa dev/sio/sio_pccard.c optional sio pccard dev/sio/sio_pci.c optional sio pci dev/sio/sio_puc.c optional sio puc dev/speaker/spkr.c optional speaker dev/syscons/apm/apm_saver.c optional apm_saver apm dev/syscons/scterm-teken.c optional sc dev/syscons/scvesactl.c optional sc vga vesa dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvtb.c optional sc dev/tpm/tpm.c optional tpm dev/tpm/tpm_acpi.c optional tpm acpi dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_x86.c optional uart dev/viawd/viawd.c optional viawd dev/vmware/vmxnet3/if_vmx.c optional vmx dev/acpica/acpi_if.m standard dev/acpi_support/acpi_wmi_if.m standard dev/wbwd/wbwd.c optional wbwd dev/wpi/if_wpi.c optional wpi dev/isci/isci.c optional isci dev/isci/isci_controller.c optional isci dev/isci/isci_domain.c optional isci dev/isci/isci_interrupt.c optional isci dev/isci/isci_io_request.c optional isci dev/isci/isci_logger.c optional isci dev/isci/isci_oem_parameters.c optional isci dev/isci/isci_remote_device.c optional isci dev/isci/isci_sysctl.c optional isci dev/isci/isci_task_request.c optional isci dev/isci/isci_timer.c optional isci dev/isci/scil/sati.c optional isci dev/isci/scil/sati_abort_task_set.c optional isci dev/isci/scil/sati_atapi.c optional isci dev/isci/scil/sati_device.c optional isci dev/isci/scil/sati_inquiry.c optional isci dev/isci/scil/sati_log_sense.c optional isci dev/isci/scil/sati_lun_reset.c optional isci dev/isci/scil/sati_mode_pages.c optional isci dev/isci/scil/sati_mode_select.c optional isci dev/isci/scil/sati_mode_sense.c optional isci dev/isci/scil/sati_mode_sense_10.c optional isci dev/isci/scil/sati_mode_sense_6.c optional isci dev/isci/scil/sati_move.c optional isci dev/isci/scil/sati_passthrough.c optional isci dev/isci/scil/sati_read.c optional isci dev/isci/scil/sati_read_buffer.c optional isci dev/isci/scil/sati_read_capacity.c optional isci dev/isci/scil/sati_reassign_blocks.c optional isci dev/isci/scil/sati_report_luns.c optional isci dev/isci/scil/sati_request_sense.c optional isci dev/isci/scil/sati_start_stop_unit.c optional isci dev/isci/scil/sati_synchronize_cache.c optional isci dev/isci/scil/sati_test_unit_ready.c optional isci dev/isci/scil/sati_unmap.c optional isci dev/isci/scil/sati_util.c optional isci dev/isci/scil/sati_verify.c optional isci dev/isci/scil/sati_write.c optional isci dev/isci/scil/sati_write_and_verify.c optional isci dev/isci/scil/sati_write_buffer.c optional isci dev/isci/scil/sati_write_long.c optional isci dev/isci/scil/sci_abstract_list.c optional isci dev/isci/scil/sci_base_controller.c optional isci dev/isci/scil/sci_base_domain.c optional isci dev/isci/scil/sci_base_iterator.c optional isci dev/isci/scil/sci_base_library.c optional isci dev/isci/scil/sci_base_logger.c optional isci dev/isci/scil/sci_base_memory_descriptor_list.c optional isci dev/isci/scil/sci_base_memory_descriptor_list_decorator.c optional isci dev/isci/scil/sci_base_object.c optional isci dev/isci/scil/sci_base_observer.c optional isci dev/isci/scil/sci_base_phy.c optional isci dev/isci/scil/sci_base_port.c optional isci dev/isci/scil/sci_base_remote_device.c optional isci dev/isci/scil/sci_base_request.c optional isci dev/isci/scil/sci_base_state_machine.c optional isci dev/isci/scil/sci_base_state_machine_logger.c optional isci dev/isci/scil/sci_base_state_machine_observer.c optional isci dev/isci/scil/sci_base_subject.c optional isci dev/isci/scil/sci_util.c optional isci dev/isci/scil/scic_sds_controller.c optional isci dev/isci/scil/scic_sds_library.c optional isci dev/isci/scil/scic_sds_pci.c optional isci dev/isci/scil/scic_sds_phy.c optional isci dev/isci/scil/scic_sds_port.c optional isci dev/isci/scil/scic_sds_port_configuration_agent.c optional isci dev/isci/scil/scic_sds_remote_device.c optional isci dev/isci/scil/scic_sds_remote_node_context.c optional isci dev/isci/scil/scic_sds_remote_node_table.c optional isci dev/isci/scil/scic_sds_request.c optional isci dev/isci/scil/scic_sds_sgpio.c optional isci dev/isci/scil/scic_sds_smp_remote_device.c optional isci dev/isci/scil/scic_sds_smp_request.c optional isci dev/isci/scil/scic_sds_ssp_request.c optional isci dev/isci/scil/scic_sds_stp_packet_request.c optional isci dev/isci/scil/scic_sds_stp_remote_device.c optional isci dev/isci/scil/scic_sds_stp_request.c optional isci dev/isci/scil/scic_sds_unsolicited_frame_control.c optional isci dev/isci/scil/scif_sas_controller.c optional isci dev/isci/scil/scif_sas_controller_state_handlers.c optional isci dev/isci/scil/scif_sas_controller_states.c optional isci dev/isci/scil/scif_sas_domain.c optional isci dev/isci/scil/scif_sas_domain_state_handlers.c optional isci dev/isci/scil/scif_sas_domain_states.c optional isci dev/isci/scil/scif_sas_high_priority_request_queue.c optional isci dev/isci/scil/scif_sas_internal_io_request.c optional isci dev/isci/scil/scif_sas_io_request.c optional isci dev/isci/scil/scif_sas_io_request_state_handlers.c optional isci dev/isci/scil/scif_sas_io_request_states.c optional isci dev/isci/scil/scif_sas_library.c optional isci dev/isci/scil/scif_sas_remote_device.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_ready_substates.c optional isci dev/isci/scil/scif_sas_remote_device_starting_substate_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_starting_substates.c optional isci dev/isci/scil/scif_sas_remote_device_state_handlers.c optional isci dev/isci/scil/scif_sas_remote_device_states.c optional isci dev/isci/scil/scif_sas_request.c optional isci dev/isci/scil/scif_sas_smp_activity_clear_affiliation.c optional isci dev/isci/scil/scif_sas_smp_io_request.c optional isci dev/isci/scil/scif_sas_smp_phy.c optional isci dev/isci/scil/scif_sas_smp_remote_device.c optional isci dev/isci/scil/scif_sas_stp_io_request.c optional isci dev/isci/scil/scif_sas_stp_remote_device.c optional isci dev/isci/scil/scif_sas_stp_task_request.c optional isci dev/isci/scil/scif_sas_task_request.c optional isci dev/isci/scil/scif_sas_task_request_state_handlers.c optional isci dev/isci/scil/scif_sas_task_request_states.c optional isci dev/isci/scil/scif_sas_timer.c optional isci i386/acpica/acpi_machdep.c optional acpi acpi_wakecode.o optional acpi \ dependency "$S/i386/acpica/acpi_wakecode.S assym.s" \ compile-with "${NORMAL_S}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.o" acpi_wakecode.bin optional acpi \ dependency "acpi_wakecode.o" \ compile-with "${OBJCOPY} -S -O binary acpi_wakecode.o ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.bin" acpi_wakecode.h optional acpi \ dependency "acpi_wakecode.bin" \ compile-with "file2c -sx 'static char wakecode[] = {' '};' < acpi_wakecode.bin > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "acpi_wakecode.h" acpi_wakedata.h optional acpi \ dependency "acpi_wakecode.o" \ compile-with '${NM} -n --defined-only acpi_wakecode.o | while read offset dummy what; do echo "#define $${what} 0x$${offset}"; done > ${.TARGET}' \ no-obj no-implicit-rule before-depend \ clean "acpi_wakedata.h" # i386/bios/apm.c optional apm i386/bios/mca_machdep.c optional mca i386/bios/smapi.c optional smapi i386/bios/smapi_bios.S optional smapi #i386/i386/apic_vector.s optional apic i386/i386/atomic.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}" i386/i386/autoconf.c standard i386/i386/bios.c optional native i386/i386/bioscall.s optional native i386/i386/bpf_jit_machdep.c optional bpf_jitter i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/elan-mmcr.c optional cpu_elan | cpu_soekris i386/i386/elf_machdep.c standard i386/i386/exception.s optional native i386/xen/exception.s optional xen i386/i386/gdb_machdep.c optional gdb i386/i386/geode.c optional cpu_geode i386/i386/i686_mem.c optional mem i386/i386/in_cksum.c optional inet | inet6 i386/i386/initcpu.c standard i386/i386/io.c optional io i386/i386/k6_mem.c optional mem i386/i386/locore.s optional native no-obj i386/xen/locore.s optional xen no-obj i386/i386/longrun.c optional cpu_enable_longrun i386/i386/machdep.c standard i386/xen/xen_machdep.c optional xen i386/i386/mem.c optional mem i386/i386/minidump_machdep.c standard i386/i386/mp_clock.c optional smp i386/i386/mp_machdep.c optional native smp i386/xen/mp_machdep.c optional xen smp i386/i386/mp_watchdog.c optional mp_watchdog smp i386/i386/mpboot.s optional smp native i386/xen/mptable.c optional apic xen i386/i386/perfmon.c optional perfmon i386/i386/pmap.c optional native i386/xen/pmap.c optional xen i386/i386/ptrace_machdep.c standard i386/i386/stack_machdep.c optional ddb | stack i386/i386/support.s standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard i386/i386/uio_machdep.c standard i386/i386/vm86.c standard i386/i386/vm_machdep.c standard i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 i386/xen/clock.c optional xen i386/isa/elink.c optional ep | ie i386/isa/npx.c optional npx i386/isa/pmtimer.c optional pmtimer i386/isa/prof_machdep.c optional profiling-routine i386/isa/spic.c optional spic i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_machdep.c optional compat_linux i386/linux/linux_ptrace.c optional compat_linux i386/linux/linux_support.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/pci/pci_cfgreg.c optional pci i386/pci/pci_pir.c optional pci i386/svr4/svr4_locore.s optional compat_svr4 \ dependency "svr4_assym.h" \ warning "COMPAT_SVR4 is broken and should be avoided" i386/svr4/svr4_machdep.c optional compat_svr4 # isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/kern_clocksource.c standard kern/imgact_aout.c optional compat_aout kern/imgact_gzip.c optional gzip kern/subr_sfbuf.c standard libkern/divdi3.c standard libkern/flsll.c standard libkern/memmove.c standard libkern/memset.c standard libkern/moddi3.c standard libkern/qdivrem.c standard libkern/ucmpdi2.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard i386/xbox/xbox.c optional xbox i386/xbox/xboxfb.c optional xboxfb dev/fb/boot_font.c optional xboxfb i386/xbox/pic16l.s optional xbox # # x86 real mode BIOS support, required by atkbdc/dpms/vesa # compat/x86bios/x86bios.c optional x86bios | atkbd | dpms | vesa # # bvm console # dev/bvm/bvm_console.c optional bvmconsole dev/bvm/bvm_dbg.c optional bvmdebug # # x86 shared code between IA32, AMD64 and PC98 architectures # x86/acpica/OsdEnvironment.c optional acpi x86/acpica/acpi_apm.c optional acpi x86/acpica/acpi_wakeup.c optional acpi x86/acpica/madt.c optional acpi apic x86/acpica/srat.c optional acpi x86/bios/smbios.c optional smbios x86/bios/vpd.c optional vpd x86/cpufreq/est.c optional cpufreq x86/cpufreq/hwpstate.c optional cpufreq x86/cpufreq/p4tcc.c optional cpufreq x86/cpufreq/powernow.c optional cpufreq x86/cpufreq/smist.c optional cpufreq x86/iommu/busdma_dmar.c optional acpi acpi_dmar pci x86/iommu/intel_ctx.c optional acpi acpi_dmar pci x86/iommu/intel_drv.c optional acpi acpi_dmar pci x86/iommu/intel_fault.c optional acpi acpi_dmar pci x86/iommu/intel_gas.c optional acpi acpi_dmar pci x86/iommu/intel_idpgtbl.c optional acpi acpi_dmar pci x86/iommu/intel_qi.c optional acpi acpi_dmar pci x86/iommu/intel_quirks.c optional acpi acpi_dmar pci x86/iommu/intel_utils.c optional acpi acpi_dmar pci x86/isa/atpic.c optional atpic x86/isa/atrtc.c optional native x86/isa/clock.c optional native x86/isa/elcr.c optional atpic | apic native x86/isa/isa.c optional isa x86/isa/isa_dma.c optional isa x86/isa/nmi.c standard x86/isa/orm.c optional isa x86/pci/pci_bus.c optional pci x86/pci/qpi.c optional pci x86/x86/busdma_bounce.c standard x86/x86/busdma_machdep.c standard x86/x86/dump_machdep.c standard x86/x86/fdt_machdep.c optional fdt x86/x86/identcpu.c standard x86/x86/intr_machdep.c standard x86/x86/io_apic.c optional apic x86/x86/legacy.c optional native x86/x86/local_apic.c optional apic x86/x86/mca.c standard x86/x86/mptable.c optional apic native x86/x86/mptable_pci.c optional apic native pci x86/x86/msi.c optional apic pci x86/x86/nexus.c standard x86/x86/tsc.c standard +x86/x86/pvclock.c standard x86/x86/delay.c standard x86/xen/hvm.c optional xenhvm x86/xen/xen_intr.c optional xen | xenhvm x86/xen/xen_apic.c optional xenhvm x86/xen/xenpv.c optional xen | xenhvm x86/xen/xen_nexus.c optional xen | xenhvm x86/xen/xen_msi.c optional xen | xenhvm Index: projects/building-blocks/sys/conf/options =================================================================== --- projects/building-blocks/sys/conf/options (revision 278195) +++ projects/building-blocks/sys/conf/options (revision 278196) @@ -1,928 +1,929 @@ # $FreeBSD$ # # On the handling of kernel options # # All kernel options should be listed in NOTES, with suitable # descriptions. Negative options (options that make some code not # compile) should be commented out; LINT (generated from NOTES) should # compile as much code as possible. Try to structure option-using # code so that a single option only switch code on, or only switch # code off, to make it possible to have a full compile-test. If # necessary, you can check for COMPILING_LINT to get maximum code # coverage. # # All new options shall also be listed in either "conf/options" or # "conf/options.". Options that affect a single source-file # .[c|s] should be directed into "opt_.h", while options # that affect multiple files should either go in "opt_global.h" if # this is a kernel-wide option (used just about everywhere), or in # "opt_.h" if it affects only some files. # Note that the effect of listing only an option without a # header-file-name in conf/options (and cousins) is that the last # convention is followed. # # This handling scheme is not yet fully implemented. # # # Format of this file: # Option name filename # # If filename is missing, the default is # opt_.h AAC_DEBUG opt_aac.h AACRAID_DEBUG opt_aacraid.h AHC_ALLOW_MEMIO opt_aic7xxx.h AHC_TMODE_ENABLE opt_aic7xxx.h AHC_DUMP_EEPROM opt_aic7xxx.h AHC_DEBUG opt_aic7xxx.h AHC_DEBUG_OPTS opt_aic7xxx.h AHC_REG_PRETTY_PRINT opt_aic7xxx.h AHD_DEBUG opt_aic79xx.h AHD_DEBUG_OPTS opt_aic79xx.h AHD_TMODE_ENABLE opt_aic79xx.h AHD_REG_PRETTY_PRINT opt_aic79xx.h ADW_ALLOW_MEMIO opt_adw.h TWA_DEBUG opt_twa.h TWA_FLASH_FIRMWARE opt_twa.h # Debugging options. ALT_BREAK_TO_DEBUGGER opt_kdb.h BREAK_TO_DEBUGGER opt_kdb.h DDB DDB_BUFR_SIZE opt_ddb.h DDB_CAPTURE_DEFAULTBUFSIZE opt_ddb.h DDB_CAPTURE_MAXBUFSIZE opt_ddb.h DDB_CTF opt_ddb.h DDB_NUMSYM opt_ddb.h GDB KDB opt_global.h KDB_TRACE opt_kdb.h KDB_UNATTENDED opt_kdb.h KLD_DEBUG opt_kld.h SYSCTL_DEBUG opt_sysctl.h EARLY_PRINTF opt_global.h TEXTDUMP_PREFERRED opt_ddb.h TEXTDUMP_VERBOSE opt_ddb.h # Miscellaneous options. ADAPTIVE_LOCKMGRS ALQ ALTERA_SDCARD_FAST_SIM opt_altera_sdcard.h ATSE_CFI_HACK opt_cfi.h AUDIT opt_global.h BOOTHOWTO opt_global.h BOOTVERBOSE opt_global.h CALLOUT_PROFILING CAPABILITIES opt_capsicum.h CAPABILITY_MODE opt_capsicum.h COMPAT_43 opt_compat.h COMPAT_43TTY opt_compat.h COMPAT_FREEBSD4 opt_compat.h COMPAT_FREEBSD5 opt_compat.h COMPAT_FREEBSD6 opt_compat.h COMPAT_FREEBSD7 opt_compat.h COMPAT_FREEBSD9 opt_compat.h COMPAT_FREEBSD10 opt_compat.h COMPAT_LINUXAPI opt_compat.h COMPILING_LINT opt_global.h COMPRESS_USER_CORES opt_core.h CY_PCI_FASTINTR DEADLKRES opt_watchdog.h DIRECTIO FILEMON opt_dontuse.h FFCLOCK FULL_PREEMPTION opt_sched.h IMAGACT_BINMISC opt_dontuse.h IPI_PREEMPTION opt_sched.h GEOM_AES opt_geom.h GEOM_BDE opt_geom.h GEOM_BSD opt_geom.h GEOM_CACHE opt_geom.h GEOM_CONCAT opt_geom.h GEOM_ELI opt_geom.h GEOM_FOX opt_geom.h GEOM_GATE opt_geom.h GEOM_JOURNAL opt_geom.h GEOM_LABEL opt_geom.h GEOM_LABEL_GPT opt_geom.h GEOM_LINUX_LVM opt_geom.h GEOM_MBR opt_geom.h GEOM_MIRROR opt_geom.h GEOM_MULTIPATH opt_geom.h GEOM_NOP opt_geom.h GEOM_PART_APM opt_geom.h GEOM_PART_BSD opt_geom.h GEOM_PART_BSD64 opt_geom.h GEOM_PART_EBR opt_geom.h GEOM_PART_EBR_COMPAT opt_geom.h GEOM_PART_GPT opt_geom.h GEOM_PART_LDM opt_geom.h GEOM_PART_MBR opt_geom.h GEOM_PART_PC98 opt_geom.h GEOM_PART_VTOC8 opt_geom.h GEOM_PC98 opt_geom.h GEOM_RAID opt_geom.h GEOM_RAID3 opt_geom.h GEOM_SHSEC opt_geom.h GEOM_STRIPE opt_geom.h GEOM_SUNLABEL opt_geom.h GEOM_UNCOMPRESS opt_geom.h GEOM_UZIP opt_geom.h GEOM_VINUM opt_geom.h GEOM_VIRSTOR opt_geom.h GEOM_VOL opt_geom.h GEOM_ZERO opt_geom.h KDTRACE_HOOKS opt_global.h KDTRACE_FRAME opt_kdtrace.h KN_HASHSIZE opt_kqueue.h KSTACK_MAX_PAGES KSTACK_PAGES KSTACK_USAGE_PROF KTRACE KTRACE_REQUEST_POOL opt_ktrace.h LIBICONV MAC opt_global.h MAC_BIBA opt_dontuse.h MAC_BSDEXTENDED opt_dontuse.h MAC_IFOFF opt_dontuse.h MAC_LOMAC opt_dontuse.h MAC_MLS opt_dontuse.h MAC_NONE opt_dontuse.h MAC_PARTITION opt_dontuse.h MAC_PORTACL opt_dontuse.h MAC_SEEOTHERUIDS opt_dontuse.h MAC_STATIC opt_mac.h MAC_STUB opt_dontuse.h MAC_TEST opt_dontuse.h MD_ROOT opt_md.h MD_ROOT_FSTYPE opt_md.h MD_ROOT_SIZE opt_md.h MFI_DEBUG opt_mfi.h MFI_DECODE_LOG opt_mfi.h MPROF_BUFFERS opt_mprof.h MPROF_HASH_SIZE opt_mprof.h NEW_PCIB opt_global.h NO_ADAPTIVE_MUTEXES opt_adaptive_mutexes.h NO_ADAPTIVE_RWLOCKS NO_ADAPTIVE_SX NO_EVENTTIMERS opt_timer.h NO_SYSCTL_DESCR opt_global.h NSWBUF_MIN opt_swap.h MBUF_PACKET_ZONE_DISABLE opt_global.h PANIC_REBOOT_WAIT_TIME opt_panic.h PPC_DEBUG opt_ppc.h PPC_PROBE_CHIPSET opt_ppc.h PPS_SYNC opt_ntp.h PREEMPTION opt_sched.h QUOTA SCHED_4BSD opt_sched.h SCHED_STATS opt_sched.h SCHED_ULE opt_sched.h SLEEPQUEUE_PROFILING SLHCI_DEBUG opt_slhci.h SPX_HACK STACK opt_stack.h SUIDDIR MSGMNB opt_sysvipc.h MSGMNI opt_sysvipc.h MSGSEG opt_sysvipc.h MSGSSZ opt_sysvipc.h MSGTQL opt_sysvipc.h SEMMNI opt_sysvipc.h SEMMNS opt_sysvipc.h SEMMNU opt_sysvipc.h SEMMSL opt_sysvipc.h SEMOPM opt_sysvipc.h SEMUME opt_sysvipc.h SHMALL opt_sysvipc.h SHMMAX opt_sysvipc.h SHMMAXPGS opt_sysvipc.h SHMMIN opt_sysvipc.h SHMMNI opt_sysvipc.h SHMSEG opt_sysvipc.h SYSVMSG opt_sysvipc.h SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h TURNSTILE_PROFILING UMTX_PROFILING VFS_AIO VERBOSE_SYSINIT opt_global.h WLCACHE opt_wavelan.h WLDEBUG opt_wavelan.h # POSIX kernel options P1003_1B_MQUEUE opt_posix.h P1003_1B_SEMAPHORES opt_posix.h _KPOSIX_PRIORITY_SCHEDULING opt_posix.h # Do we want the config file compiled into the kernel? INCLUDE_CONFIG_FILE opt_config.h # Options for static filesystems. These should only be used at config # time, since the corresponding lkms cannot work if there are any static # dependencies. Unusability is enforced by hiding the defines for the # options in a never-included header. AUTOFS opt_dontuse.h CD9660 opt_dontuse.h EXT2FS opt_dontuse.h FDESCFS opt_dontuse.h FFS opt_dontuse.h FUSE opt_dontuse.h MSDOSFS opt_dontuse.h NANDFS opt_dontuse.h NULLFS opt_dontuse.h PROCFS opt_dontuse.h PSEUDOFS opt_dontuse.h REISERFS opt_dontuse.h SMBFS opt_dontuse.h TMPFS opt_dontuse.h UDF opt_dontuse.h UNIONFS opt_dontuse.h ZFS opt_dontuse.h # Pseudofs debugging PSEUDOFS_TRACE opt_pseudofs.h # In-kernel GSS-API KGSSAPI opt_kgssapi.h KGSSAPI_DEBUG opt_kgssapi.h # These static filesystems have one slightly bogus static dependency in # sys/i386/i386/autoconf.c. If any of these filesystems are # statically compiled into the kernel, code for mounting them as root # filesystems will be enabled - but look below. # NFSCL - client # NFSD - server NFSCL opt_nfs.h NFSD opt_nfs.h # filesystems and libiconv bridge CD9660_ICONV opt_dontuse.h MSDOSFS_ICONV opt_dontuse.h UDF_ICONV opt_dontuse.h # If you are following the conditions in the copyright, # you can enable soft-updates which will speed up a lot of thigs # and make the system safer from crashes at the same time. # otherwise a STUB module will be compiled in. SOFTUPDATES opt_ffs.h # On small, embedded systems, it can be useful to turn off support for # snapshots. It saves about 30-40k for a feature that would be lightly # used, if it is used at all. NO_FFS_SNAPSHOT opt_ffs.h # Enabling this option turns on support for Access Control Lists in UFS, # which can be used to support high security configurations. Depends on # UFS_EXTATTR. UFS_ACL opt_ufs.h # Enabling this option turns on support for extended attributes in UFS-based # filesystems, which can be used to support high security configurations # as well as new filesystem features. UFS_EXTATTR opt_ufs.h UFS_EXTATTR_AUTOSTART opt_ufs.h # Enable fast hash lookups for large directories on UFS-based filesystems. UFS_DIRHASH opt_ufs.h # Enable gjournal-based UFS journal. UFS_GJOURNAL opt_ufs.h # The below sentence is not in English, and neither is this one. # We plan to remove the static dependences above, with a # _ROOT option to control if it usable as root. This list # allows these options to be present in config files already (though # they won't make any difference yet). NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h NBUF opt_param.h NSFBUFS opt_param.h VM_BCACHE_SIZE_MAX opt_param.h VM_SWZONE_SIZE_MAX opt_param.h MAXUSERS DFLDSIZ opt_param.h MAXDSIZ opt_param.h MAXSSIZ opt_param.h # Generic SCSI options. CAM_MAX_HIGHPOWER opt_cam.h CAMDEBUG opt_cam.h CAM_DEBUG_COMPILE opt_cam.h CAM_DEBUG_DELAY opt_cam.h CAM_DEBUG_BUS opt_cam.h CAM_DEBUG_TARGET opt_cam.h CAM_DEBUG_LUN opt_cam.h CAM_DEBUG_FLAGS opt_cam.h CAM_BOOT_DELAY opt_cam.h SCSI_DELAY opt_scsi.h SCSI_NO_SENSE_STRINGS opt_scsi.h SCSI_NO_OP_STRINGS opt_scsi.h # Options used only in cam/ata/ata_da.c ADA_TEST_FAILURE opt_ada.h ATA_STATIC_ID opt_ada.h # Options used only in cam/scsi/scsi_cd.c CHANGER_MIN_BUSY_SECONDS opt_cd.h CHANGER_MAX_BUSY_SECONDS opt_cd.h # Options used only in cam/scsi/scsi_sa.c. SA_IO_TIMEOUT opt_sa.h SA_SPACE_TIMEOUT opt_sa.h SA_REWIND_TIMEOUT opt_sa.h SA_ERASE_TIMEOUT opt_sa.h SA_1FM_AT_EOD opt_sa.h # Options used only in cam/scsi/scsi_pt.c SCSI_PT_DEFAULT_TIMEOUT opt_pt.h # Options used only in cam/scsi/scsi_ses.c SES_ENABLE_PASSTHROUGH opt_ses.h # Options used in dev/sym/ (Symbios SCSI driver). SYM_SETUP_LP_PROBE_MAP opt_sym.h #-Low Priority Probe Map (bits) # Allows the ncr to take precedence # 1 (1<<0) -> 810a, 860 # 2 (1<<1) -> 825a, 875, 885, 895 # 4 (1<<2) -> 895a, 896, 1510d SYM_SETUP_SCSI_DIFF opt_sym.h #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 SYM_SETUP_PCI_PARITY opt_sym.h #-PCI parity checking # disabled:0, enabled:1 (default) SYM_SETUP_MAX_LUN opt_sym.h #-Number of LUNs supported # default:8, range:[1..64] # Options used only in dev/ncr/* SCSI_NCR_DEBUG opt_ncr.h SCSI_NCR_MAX_SYNC opt_ncr.h SCSI_NCR_MAX_WIDE opt_ncr.h SCSI_NCR_MYADDR opt_ncr.h # Options used only in dev/isp/* ISP_TARGET_MODE opt_isp.h ISP_FW_CRASH_DUMP opt_isp.h ISP_DEFAULT_ROLES opt_isp.h ISP_INTERNAL_TARGET opt_isp.h # Options used only in dev/iscsi ISCSI_INITIATOR_DEBUG opt_iscsi_initiator.h # Net stuff. ACCEPT_FILTER_DATA ACCEPT_FILTER_DNS ACCEPT_FILTER_HTTP ALTQ opt_global.h ALTQ_CBQ opt_altq.h ALTQ_CDNR opt_altq.h ALTQ_DEBUG opt_altq.h ALTQ_HFSC opt_altq.h ALTQ_NOPCC opt_altq.h ALTQ_PRIQ opt_altq.h ALTQ_RED opt_altq.h ALTQ_RIO opt_altq.h BOOTP opt_bootp.h BOOTP_BLOCKSIZE opt_bootp.h BOOTP_COMPAT opt_bootp.h BOOTP_NFSROOT opt_bootp.h BOOTP_NFSV3 opt_bootp.h BOOTP_WIRED_TO opt_bootp.h DEVICE_POLLING DUMMYNET opt_ipdn.h INET opt_inet.h INET6 opt_inet6.h IPDIVERT IPFILTER opt_ipfilter.h IPFILTER_DEFAULT_BLOCK opt_ipfilter.h IPFILTER_LOG opt_ipfilter.h IPFILTER_LOOKUP opt_ipfilter.h IPFIREWALL opt_ipfw.h IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h IPFIREWALL_NAT opt_ipfw.h IPFIREWALL_VERBOSE opt_ipfw.h IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h IPSEC opt_ipsec.h IPSEC_DEBUG opt_ipsec.h IPSEC_FILTERTUNNEL opt_ipsec.h IPSEC_NAT_T opt_ipsec.h IPSTEALTH KRPC LIBALIAS LIBMBPOOL LIBMCHAIN MBUF_PROFILING MBUF_STRESS_TEST MROUTING opt_mrouting.h NFSLOCKD PCBGROUP opt_pcbgroup.h PF_DEFAULT_TO_DROP opt_pf.h RADIX_MPATH opt_mpath.h ROUTETABLES opt_route.h RSS opt_rss.h SLIP_IFF_OPTS opt_slip.h TCPDEBUG TCP_OFFLOAD opt_inet.h # Enable code to dispatch TCP offloading TCP_SIGNATURE opt_inet.h VLAN_ARRAY opt_vlan.h XBONEHACK FLOWTABLE opt_route.h FLOWTABLE_HASH_ALL opt_route.h # # SCTP # SCTP opt_sctp.h SCTP_DEBUG opt_sctp.h # Enable debug printfs SCTP_WITH_NO_CSUM opt_sctp.h # Use this at your peril SCTP_LOCK_LOGGING opt_sctp.h # Log to KTR lock activity SCTP_MBUF_LOGGING opt_sctp.h # Log to KTR general mbuf aloc/free SCTP_MBCNT_LOGGING opt_sctp.h # Log to KTR mbcnt activity SCTP_PACKET_LOGGING opt_sctp.h # Log to a packet buffer last N packets SCTP_LTRACE_CHUNKS opt_sctp.h # Log to KTR chunks processed SCTP_LTRACE_ERRORS opt_sctp.h # Log to KTR error returns. SCTP_USE_PERCPU_STAT opt_sctp.h # Use per cpu stats. SCTP_MCORE_INPUT opt_sctp.h # Have multiple input threads for input mbufs SCTP_LOCAL_TRACE_BUF opt_sctp.h # Use tracebuffer exported via sysctl SCTP_DETAILED_STR_STATS opt_sctp.h # Use per PR-SCTP policy stream stats # # # # Netgraph(4). Use option NETGRAPH to enable the base netgraph code. # Each netgraph node type can be either be compiled into the kernel # or loaded dynamically. To get the former, include the corresponding # option below. Each type has its own man page, e.g. ng_async(4). NETGRAPH NETGRAPH_DEBUG opt_netgraph.h NETGRAPH_ASYNC opt_netgraph.h NETGRAPH_ATMLLC opt_netgraph.h NETGRAPH_ATM_ATMPIF opt_netgraph.h NETGRAPH_BLUETOOTH opt_netgraph.h NETGRAPH_BLUETOOTH_BT3C opt_netgraph.h NETGRAPH_BLUETOOTH_H4 opt_netgraph.h NETGRAPH_BLUETOOTH_HCI opt_netgraph.h NETGRAPH_BLUETOOTH_L2CAP opt_netgraph.h NETGRAPH_BLUETOOTH_SOCKET opt_netgraph.h NETGRAPH_BLUETOOTH_UBT opt_netgraph.h NETGRAPH_BLUETOOTH_UBTBCMFW opt_netgraph.h NETGRAPH_BPF opt_netgraph.h NETGRAPH_BRIDGE opt_netgraph.h NETGRAPH_CAR opt_netgraph.h NETGRAPH_CISCO opt_netgraph.h NETGRAPH_DEFLATE opt_netgraph.h NETGRAPH_DEVICE opt_netgraph.h NETGRAPH_ECHO opt_netgraph.h NETGRAPH_EIFACE opt_netgraph.h NETGRAPH_ETHER opt_netgraph.h NETGRAPH_ETHER_ECHO opt_netgraph.h NETGRAPH_FEC opt_netgraph.h NETGRAPH_FRAME_RELAY opt_netgraph.h NETGRAPH_GIF opt_netgraph.h NETGRAPH_GIF_DEMUX opt_netgraph.h NETGRAPH_HOLE opt_netgraph.h NETGRAPH_IFACE opt_netgraph.h NETGRAPH_IP_INPUT opt_netgraph.h NETGRAPH_IPFW opt_netgraph.h NETGRAPH_KSOCKET opt_netgraph.h NETGRAPH_L2TP opt_netgraph.h NETGRAPH_LMI opt_netgraph.h # MPPC compression requires proprietary files (not included) NETGRAPH_MPPC_COMPRESSION opt_netgraph.h NETGRAPH_MPPC_ENCRYPTION opt_netgraph.h NETGRAPH_NAT opt_netgraph.h NETGRAPH_NETFLOW opt_netgraph.h NETGRAPH_ONE2MANY opt_netgraph.h NETGRAPH_PATCH opt_netgraph.h NETGRAPH_PIPE opt_netgraph.h NETGRAPH_PPP opt_netgraph.h NETGRAPH_PPPOE opt_netgraph.h NETGRAPH_PPTPGRE opt_netgraph.h NETGRAPH_PRED1 opt_netgraph.h NETGRAPH_RFC1490 opt_netgraph.h NETGRAPH_SOCKET opt_netgraph.h NETGRAPH_SPLIT opt_netgraph.h NETGRAPH_SPPP opt_netgraph.h NETGRAPH_TAG opt_netgraph.h NETGRAPH_TCPMSS opt_netgraph.h NETGRAPH_TEE opt_netgraph.h NETGRAPH_TTY opt_netgraph.h NETGRAPH_UI opt_netgraph.h NETGRAPH_VJC opt_netgraph.h NETGRAPH_VLAN opt_netgraph.h # NgATM options NGATM_ATM opt_netgraph.h NGATM_ATMBASE opt_netgraph.h NGATM_SSCOP opt_netgraph.h NGATM_SSCFU opt_netgraph.h NGATM_UNI opt_netgraph.h NGATM_CCATM opt_netgraph.h # DRM options DRM_DEBUG opt_drm.h TI_SF_BUF_JUMBO opt_ti.h TI_JUMBO_HDRSPLIT opt_ti.h # XXX Conflict: # of devices vs network protocol (Native ATM). # This makes "atm.h" unusable. NATM # DPT driver debug flags DPT_MEASURE_PERFORMANCE opt_dpt.h DPT_RESET_HBA opt_dpt.h # Misc debug flags. Most of these should probably be replaced with # 'DEBUG', and then let people recompile just the interesting modules # with 'make CC="cc -DDEBUG"'. CLUSTERDEBUG opt_debug_cluster.h DEBUG_1284 opt_ppb_1284.h VP0_DEBUG opt_vpo.h LPT_DEBUG opt_lpt.h PLIP_DEBUG opt_plip.h LOCKF_DEBUG opt_debug_lockf.h SI_DEBUG opt_debug_si.h # Fb options FB_DEBUG opt_fb.h FB_INSTALL_CDEV opt_fb.h # ppbus related options PERIPH_1284 opt_ppb_1284.h DONTPROBE_1284 opt_ppb_1284.h # smbus related options ENABLE_ALART opt_intpm.h # These cause changes all over the kernel BLKDEV_IOSIZE opt_global.h BURN_BRIDGES opt_global.h DEBUG opt_global.h DEBUG_LOCKS opt_global.h DEBUG_VFS_LOCKS opt_global.h DFLTPHYS opt_global.h DIAGNOSTIC opt_global.h INVARIANT_SUPPORT opt_global.h INVARIANTS opt_global.h MAXCPU opt_global.h MAXMEMDOM opt_global.h MAXPHYS opt_global.h MCLSHIFT opt_global.h MUTEX_DEBUG opt_global.h MUTEX_NOINLINE opt_global.h LOCK_PROFILING opt_global.h LOCK_PROFILING_FAST opt_global.h MSIZE opt_global.h REGRESSION opt_global.h RWLOCK_NOINLINE opt_global.h SX_NOINLINE opt_global.h VFS_BIO_DEBUG opt_global.h # These are VM related options VM_KMEM_SIZE opt_vm.h VM_KMEM_SIZE_SCALE opt_vm.h VM_KMEM_SIZE_MAX opt_vm.h VM_NRESERVLEVEL opt_vm.h VM_LEVEL_0_ORDER opt_vm.h NO_SWAPPING opt_vm.h MALLOC_MAKE_FAILURES opt_vm.h MALLOC_PROFILE opt_vm.h MALLOC_DEBUG_MAXZONES opt_vm.h # The MemGuard replacement allocator used for tamper-after-free detection DEBUG_MEMGUARD opt_vm.h # The RedZone malloc(9) protection DEBUG_REDZONE opt_vm.h # Standard SMP options SMP opt_global.h # Size of the kernel message buffer MSGBUF_SIZE opt_msgbuf.h # NFS options NFS_MINATTRTIMO opt_nfs.h NFS_MAXATTRTIMO opt_nfs.h NFS_MINDIRATTRTIMO opt_nfs.h NFS_MAXDIRATTRTIMO opt_nfs.h NFS_DEBUG opt_nfs.h # For the Bt848/Bt848A/Bt849/Bt878/Bt879 driver OVERRIDE_CARD opt_bktr.h OVERRIDE_TUNER opt_bktr.h OVERRIDE_DBX opt_bktr.h OVERRIDE_MSP opt_bktr.h BROOKTREE_SYSTEM_DEFAULT opt_bktr.h BROOKTREE_ALLOC_PAGES opt_bktr.h BKTR_OVERRIDE_CARD opt_bktr.h BKTR_OVERRIDE_TUNER opt_bktr.h BKTR_OVERRIDE_DBX opt_bktr.h BKTR_OVERRIDE_MSP opt_bktr.h BKTR_SYSTEM_DEFAULT opt_bktr.h BKTR_ALLOC_PAGES opt_bktr.h BKTR_USE_PLL opt_bktr.h BKTR_GPIO_ACCESS opt_bktr.h BKTR_NO_MSP_RESET opt_bktr.h BKTR_430_FX_MODE opt_bktr.h BKTR_SIS_VIA_MODE opt_bktr.h BKTR_USE_FREEBSD_SMBUS opt_bktr.h BKTR_NEW_MSP34XX_DRIVER opt_bktr.h # Options for uart(4) UART_PPS_ON_CTS opt_uart.h UART_POLL_FREQ opt_uart.h # options for bus/device framework BUS_DEBUG opt_bus.h # options for USB support USB_DEBUG opt_usb.h USB_HOST_ALIGN opt_usb.h USB_REQ_DEBUG opt_usb.h USB_TEMPLATE opt_usb.h USB_VERBOSE opt_usb.h +USB_DMA_SINGLE_ALLOC opt_usb.h USB_EHCI_BIG_ENDIAN_DESC opt_usb.h U3G_DEBUG opt_u3g.h UKBD_DFLT_KEYMAP opt_ukbd.h UPLCOM_INTR_INTERVAL opt_uplcom.h UVSCOM_DEFAULT_OPKTSIZE opt_uvscom.h UVSCOM_INTR_INTERVAL opt_uvscom.h # Embedded system options INIT_PATH ROOTDEVNAME FDC_DEBUG opt_fdc.h PCFCLOCK_VERBOSE opt_pcfclock.h PCFCLOCK_MAX_RETRIES opt_pcfclock.h KTR opt_global.h KTR_ALQ opt_ktr.h KTR_MASK opt_ktr.h KTR_CPUMASK opt_ktr.h KTR_COMPILE opt_global.h KTR_BOOT_ENTRIES opt_global.h KTR_ENTRIES opt_global.h KTR_VERBOSE opt_ktr.h WITNESS opt_global.h WITNESS_KDB opt_witness.h WITNESS_NO_VNODE opt_witness.h WITNESS_SKIPSPIN opt_witness.h WITNESS_COUNT opt_witness.h OPENSOLARIS_WITNESS opt_global.h # options for ACPI support ACPI_DEBUG opt_acpi.h ACPI_MAX_TASKS opt_acpi.h ACPI_MAX_THREADS opt_acpi.h ACPI_DMAR opt_acpi.h # ISA support DEV_ISA opt_isa.h ISAPNP opt_isa.h # various 'device presence' options. DEV_BPF opt_bpf.h DEV_CARP opt_carp.h DEV_ENC opt_enc.h DEV_MCA opt_mca.h DEV_NETMAP opt_global.h DEV_PCI opt_pci.h DEV_PF opt_pf.h DEV_PFLOG opt_pf.h DEV_PFSYNC opt_pf.h DEV_SPLASH opt_splash.h DEV_VLAN opt_vlan.h # EISA support DEV_EISA opt_eisa.h EISA_SLOTS opt_eisa.h # ed driver ED_HPP opt_ed.h ED_3C503 opt_ed.h ED_SIC opt_ed.h # bce driver BCE_DEBUG opt_bce.h BCE_NVRAM_WRITE_SUPPORT opt_bce.h SOCKBUF_DEBUG opt_global.h # options for ubsec driver UBSEC_DEBUG opt_ubsec.h UBSEC_RNDTEST opt_ubsec.h UBSEC_NO_RNG opt_ubsec.h # options for hifn driver HIFN_DEBUG opt_hifn.h HIFN_RNDTEST opt_hifn.h # options for safenet driver SAFE_DEBUG opt_safe.h SAFE_NO_RNG opt_safe.h SAFE_RNDTEST opt_safe.h # syscons/vt options MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_CUT_SPACES2TABS opt_syscons.h SC_CUT_SEPCHARS opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DISABLE_KDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_MODE_CHANGE opt_syscons.h SC_NO_SUSPEND_VTYSWITCH opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h VT_ALT_TO_ESC_HACK opt_syscons.h VT_FB_DEFAULT_WIDTH opt_syscons.h VT_FB_DEFAULT_HEIGHT opt_syscons.h VT_MAXWINDOWS opt_syscons.h VT_TWOBUTTON_MOUSE opt_syscons.h DEV_SC opt_syscons.h DEV_VT opt_syscons.h # teken terminal emulator options TEKEN_CONS25 opt_teken.h TEKEN_UTF8 opt_teken.h TERMINAL_KERN_ATTR opt_teken.h TERMINAL_NORM_ATTR opt_teken.h # options for printf PRINTF_BUFR_SIZE opt_printf.h # kbd options KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBDIO_DEBUG opt_kbd.h # options for the Atheros driver ATH_DEBUG opt_ath.h ATH_TXBUF opt_ath.h ATH_RXBUF opt_ath.h ATH_DIAGAPI opt_ath.h ATH_TX99_DIAG opt_ath.h ATH_ENABLE_11N opt_ath.h ATH_ENABLE_DFS opt_ath.h ATH_EEPROM_FIRMWARE opt_ath.h ATH_ENABLE_RADIOTAP_VENDOR_EXT opt_ath.h ATH_DEBUG_ALQ opt_ath.h ATH_KTR_INTR_DEBUG opt_ath.h # options for the Atheros hal AH_SUPPORT_AR5416 opt_ah.h # XXX For now, this breaks non-AR9130 chipsets, so only use it # XXX when actually targetting AR9130. AH_SUPPORT_AR9130 opt_ah.h # This is required for AR933x SoC support AH_SUPPORT_AR9330 opt_ah.h AH_SUPPORT_AR9340 opt_ah.h AH_SUPPORT_QCA9550 opt_ah.h AH_DEBUG opt_ah.h AH_ASSERT opt_ah.h AH_DEBUG_ALQ opt_ah.h AH_REGOPS_FUNC opt_ah.h AH_WRITE_REGDOMAIN opt_ah.h AH_DEBUG_COUNTRY opt_ah.h AH_WRITE_EEPROM opt_ah.h AH_PRIVATE_DIAG opt_ah.h AH_NEED_DESC_SWAP opt_ah.h AH_USE_INIPDGAIN opt_ah.h AH_MAXCHAN opt_ah.h AH_RXCFG_SDMAMW_4BYTES opt_ah.h AH_INTERRUPT_DEBUGGING opt_ah.h # AR5416 and later interrupt mitigation # XXX do not use this for AR9130 AH_AR5416_INTERRUPT_MITIGATION opt_ah.h # options for the Broadcom BCM43xx driver (bwi) BWI_DEBUG opt_bwi.h BWI_DEBUG_VERBOSE opt_bwi.h # options for the Marvell 8335 wireless driver MALO_DEBUG opt_malo.h MALO_TXBUF opt_malo.h MALO_RXBUF opt_malo.h # options for the Marvell wireless driver MWL_DEBUG opt_mwl.h MWL_TXBUF opt_mwl.h MWL_RXBUF opt_mwl.h MWL_DIAGAPI opt_mwl.h MWL_AGGR_SIZE opt_mwl.h MWL_TX_NODROP opt_mwl.h # Options for the Intel 802.11n wireless driver IWN_DEBUG opt_iwn.h # dcons options DCONS_BUF_SIZE opt_dcons.h DCONS_POLL_HZ opt_dcons.h DCONS_FORCE_CONSOLE opt_dcons.h DCONS_FORCE_GDB opt_dcons.h # HWPMC options HWPMC_HOOKS HWPMC_MIPS_BACKTRACE opt_hwpmc_hooks.h # XBOX options for FreeBSD/i386, but some files are MI XBOX opt_xbox.h # Interrupt filtering INTR_FILTER # 802.11 support layer IEEE80211_DEBUG opt_wlan.h IEEE80211_DEBUG_REFCNT opt_wlan.h IEEE80211_AMPDU_AGE opt_wlan.h IEEE80211_SUPPORT_MESH opt_wlan.h IEEE80211_SUPPORT_SUPERG opt_wlan.h IEEE80211_SUPPORT_TDMA opt_wlan.h IEEE80211_ALQ opt_wlan.h IEEE80211_DFS_DEBUG opt_wlan.h # 802.11 TDMA support TDMA_SLOTLEN_DEFAULT opt_tdma.h TDMA_SLOTCNT_DEFAULT opt_tdma.h TDMA_BINTVAL_DEFAULT opt_tdma.h TDMA_TXRATE_11B_DEFAULT opt_tdma.h TDMA_TXRATE_11G_DEFAULT opt_tdma.h TDMA_TXRATE_11A_DEFAULT opt_tdma.h TDMA_TXRATE_TURBO_DEFAULT opt_tdma.h TDMA_TXRATE_HALF_DEFAULT opt_tdma.h TDMA_TXRATE_QUARTER_DEFAULT opt_tdma.h TDMA_TXRATE_11NA_DEFAULT opt_tdma.h TDMA_TXRATE_11NG_DEFAULT opt_tdma.h # Network stack virtualization options VIMAGE opt_global.h VNET_DEBUG opt_global.h # Common Flash Interface (CFI) options CFI_SUPPORT_STRATAFLASH opt_cfi.h CFI_ARMEDANDDANGEROUS opt_cfi.h # Sound options SND_DEBUG opt_snd.h SND_DIAGNOSTIC opt_snd.h SND_FEEDER_MULTIFORMAT opt_snd.h SND_FEEDER_FULL_MULTIFORMAT opt_snd.h SND_FEEDER_RATE_HP opt_snd.h SND_PCM_64 opt_snd.h SND_OLDSTEREO opt_snd.h X86BIOS # Flattened device tree options FDT opt_platform.h FDT_DTB_STATIC opt_platform.h # OFED Infiniband stack OFED opt_ofed.h OFED_DEBUG_INIT opt_ofed.h SDP opt_ofed.h SDP_DEBUG opt_ofed.h IPOIB opt_ofed.h IPOIB_DEBUG opt_ofed.h IPOIB_CM opt_ofed.h # Resource Accounting RACCT opt_global.h # Resource Limits RCTL opt_global.h # Random number generator(s) RANDOM_YARROW opt_random.h RANDOM_FORTUNA opt_random.h RANDOM_DEBUG opt_random.h Index: projects/building-blocks/sys/conf =================================================================== --- projects/building-blocks/sys/conf (revision 278195) +++ projects/building-blocks/sys/conf (revision 278196) Property changes on: projects/building-blocks/sys/conf ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys/conf:r278050-278194 Index: projects/building-blocks/sys/dev/drm2/i915/i915_drv.c =================================================================== --- projects/building-blocks/sys/dev/drm2/i915/i915_drv.c (revision 278195) +++ projects/building-blocks/sys/dev/drm2/i915/i915_drv.c (revision 278196) @@ -1,994 +1,1009 @@ /* i915_drv.c -- Intel i915 driver -*- linux-c -*- * Created: Wed Feb 14 17:10:04 2001 by gareth@valinux.com */ /*- * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (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 (including the next * paragraph) 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 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. * * Authors: * Gareth Hughes * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "fb_if.h" /* drv_PCI_IDs comes from drm_pciids.h, generated from drm_pciids.txt. */ static drm_pci_id_list_t i915_pciidlist[] = { i915_PCI_IDS }; static const struct intel_device_info intel_i830_info = { .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_845g_info = { .gen = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i85x_info = { .gen = 2, .is_i85x = 1, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i865g_info = { .gen = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915g_info = { .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915gm_info = { .gen = 3, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i945g_info = { .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i945gm_info = { .gen = 3, .is_i945gm = 1, .is_mobile = 1, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i965g_info = { .gen = 4, .is_broadwater = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_i965gm_info = { .gen = 4, .is_crestline = 1, .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1, .has_overlay = 1, .supports_tv = 1, }; static const struct intel_device_info intel_g33_info = { .gen = 3, .is_g33 = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_g45_info = { .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_gm45_info = { .gen = 4, .is_g4x = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, .supports_tv = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_pineview_info = { .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_ironlake_d_info = { .gen = 5, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ironlake_m_info = { .gen = 5, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, /* disabled due to buggy hardware */ .has_bsd_ring = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_sandybridge_d_info = { .gen = 6, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_sandybridge_m_info = { .gen = 6, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ivybridge_d_info = { .is_ivybridge = 1, .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { .is_ivybridge = 1, .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */ .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; #if 0 static const struct intel_device_info intel_valleyview_m_info = { .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, }; static const struct intel_device_info intel_valleyview_d_info = { .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, }; #endif static const struct intel_device_info intel_haswell_d_info = { .is_haswell = 1, .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, + .not_supported = 1, }; static const struct intel_device_info intel_haswell_m_info = { .is_haswell = 1, .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, + .not_supported = 1, }; #define INTEL_VGA_DEVICE(id, info_) { \ .device = id, \ .info = info_, \ } static const struct intel_gfx_device_id { int device; const struct intel_device_info *info; } pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x3577, &intel_i830_info), INTEL_VGA_DEVICE(0x2562, &intel_845g_info), INTEL_VGA_DEVICE(0x3582, &intel_i85x_info), INTEL_VGA_DEVICE(0x358e, &intel_i85x_info), INTEL_VGA_DEVICE(0x2572, &intel_i865g_info), INTEL_VGA_DEVICE(0x2582, &intel_i915g_info), INTEL_VGA_DEVICE(0x258a, &intel_i915g_info), INTEL_VGA_DEVICE(0x2592, &intel_i915gm_info), INTEL_VGA_DEVICE(0x2772, &intel_i945g_info), INTEL_VGA_DEVICE(0x27a2, &intel_i945gm_info), INTEL_VGA_DEVICE(0x27ae, &intel_i945gm_info), INTEL_VGA_DEVICE(0x2972, &intel_i965g_info), INTEL_VGA_DEVICE(0x2982, &intel_i965g_info), INTEL_VGA_DEVICE(0x2992, &intel_i965g_info), INTEL_VGA_DEVICE(0x29a2, &intel_i965g_info), INTEL_VGA_DEVICE(0x29b2, &intel_g33_info), INTEL_VGA_DEVICE(0x29c2, &intel_g33_info), INTEL_VGA_DEVICE(0x29d2, &intel_g33_info), INTEL_VGA_DEVICE(0x2a02, &intel_i965gm_info), INTEL_VGA_DEVICE(0x2a12, &intel_i965gm_info), INTEL_VGA_DEVICE(0x2a42, &intel_gm45_info), INTEL_VGA_DEVICE(0x2e02, &intel_g45_info), INTEL_VGA_DEVICE(0x2e12, &intel_g45_info), INTEL_VGA_DEVICE(0x2e22, &intel_g45_info), INTEL_VGA_DEVICE(0x2e32, &intel_g45_info), INTEL_VGA_DEVICE(0x2e42, &intel_g45_info), INTEL_VGA_DEVICE(0x2e92, &intel_g45_info), INTEL_VGA_DEVICE(0xa001, &intel_pineview_info), INTEL_VGA_DEVICE(0xa011, &intel_pineview_info), INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info), INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info), INTEL_VGA_DEVICE(0x0102, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0112, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0122, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0106, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x0116, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x0126, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x010A, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0156, &intel_ivybridge_m_info), /* GT1 mobile */ INTEL_VGA_DEVICE(0x0166, &intel_ivybridge_m_info), /* GT2 mobile */ INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */ INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */ INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */ INTEL_VGA_DEVICE(0x0c16, &intel_haswell_d_info), /* SDV */ {0, 0} }; +static int i915_enable_unsupported; + static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv; int error; dev_priv = dev->dev_private; drm_kms_helper_poll_disable(dev); #if 0 pci_save_state(dev->pdev); #endif DRM_LOCK(dev); /* If KMS is active, we do the leavevt stuff here */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { error = -i915_gem_idle(dev); if (error) { DRM_UNLOCK(dev); device_printf(dev->device, "GEM idle failed, resume might fail\n"); return (error); } drm_irq_uninstall(dev); } i915_save_state(dev); intel_opregion_fini(dev); /* Modeset on resume, not lid events */ dev_priv->modeset_on_lid = 0; DRM_UNLOCK(dev); return 0; } static int i915_suspend(device_t kdev) { struct drm_device *dev; int error; dev = device_get_softc(kdev); if (dev == NULL || dev->dev_private == NULL) { DRM_ERROR("DRM not initialized, aborting suspend.\n"); return -ENODEV; } DRM_DEBUG_KMS("starting suspend\n"); error = i915_drm_freeze(dev); if (error) return (error); error = bus_generic_suspend(kdev); DRM_DEBUG_KMS("finished suspend %d\n", error); return (error); } static int i915_drm_thaw(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int error = 0; DRM_LOCK(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { i915_gem_restore_gtt_mappings(dev); } i915_restore_state(dev); intel_opregion_setup(dev); /* KMS EnterVT equivalent */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (HAS_PCH_SPLIT(dev)) ironlake_init_pch_refclk(dev); dev_priv->mm.suspended = 0; error = i915_gem_init_hw(dev); DRM_UNLOCK(dev); intel_modeset_init_hw(dev); sx_xlock(&dev->mode_config.mutex); drm_mode_config_reset(dev); sx_xunlock(&dev->mode_config.mutex); drm_irq_install(dev); sx_xlock(&dev->mode_config.mutex); /* Resume the modeset for every activated CRTC */ drm_helper_resume_force_mode(dev); sx_xunlock(&dev->mode_config.mutex); DRM_LOCK(dev); } intel_opregion_init(dev); dev_priv->modeset_on_lid = 0; DRM_UNLOCK(dev); return error; } static int i915_resume(device_t kdev) { struct drm_device *dev; int ret; dev = device_get_softc(kdev); DRM_DEBUG_KMS("starting resume\n"); #if 0 if (pci_enable_device(dev->pdev)) return -EIO; pci_set_master(dev->pdev); #endif ret = -i915_drm_thaw(dev); if (ret != 0) return (ret); drm_kms_helper_poll_enable(dev); ret = bus_generic_resume(kdev); DRM_DEBUG_KMS("finished resume %d\n", ret); return (ret); } static int i915_probe(device_t kdev) { + const struct intel_device_info *info; + int error; - return drm_probe(kdev, i915_pciidlist); + error = drm_probe(kdev, i915_pciidlist); + if (error != 0) + return (error); + info = i915_get_device_id(pci_get_device(kdev)); + if (info == NULL) + return (ENXIO); + return (0); } int i915_modeset; static int i915_attach(device_t kdev) { struct drm_device *dev; dev = device_get_softc(kdev); if (i915_modeset == 1) i915_driver_info.driver_features |= DRIVER_MODESET; dev->driver = &i915_driver_info; return (drm_attach(kdev, i915_pciidlist)); } static struct fb_info * i915_fb_helper_getinfo(device_t kdev) { struct intel_fbdev *ifbdev; drm_i915_private_t *dev_priv; struct drm_device *dev; struct fb_info *info; dev = device_get_softc(kdev); dev_priv = dev->dev_private; ifbdev = dev_priv->fbdev; if (ifbdev == NULL) return (NULL); info = ifbdev->helper.fbdev; return (info); } const struct intel_device_info * i915_get_device_id(int device) { const struct intel_gfx_device_id *did; for (did = &pciidlist[0]; did->device != 0; did++) { if (did->device != device) continue; + if (did->info->not_supported && !i915_enable_unsupported) + return (NULL); return (did->info); } return (NULL); } static device_method_t i915_methods[] = { /* Device interface */ DEVMETHOD(device_probe, i915_probe), DEVMETHOD(device_attach, i915_attach), DEVMETHOD(device_suspend, i915_suspend), DEVMETHOD(device_resume, i915_resume), DEVMETHOD(device_detach, drm_detach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, i915_fb_helper_getinfo), DEVMETHOD_END }; static driver_t i915_driver = { "drmn", i915_methods, sizeof(struct drm_device) }; extern devclass_t drm_devclass; DRIVER_MODULE_ORDERED(i915kms, vgapci, i915_driver, drm_devclass, 0, 0, SI_ORDER_ANY); MODULE_DEPEND(i915kms, drmn, 1, 1, 1); MODULE_DEPEND(i915kms, agp, 1, 1, 1); MODULE_DEPEND(i915kms, iicbus, 1, 1, 1); MODULE_DEPEND(i915kms, iic, 1, 1, 1); MODULE_DEPEND(i915kms, iicbb, 1, 1, 1); int intel_iommu_enabled = 0; TUNABLE_INT("drm.i915.intel_iommu_enabled", &intel_iommu_enabled); int intel_iommu_gfx_mapped = 0; TUNABLE_INT("drm.i915.intel_iommu_gfx_mapped", &intel_iommu_gfx_mapped); int i915_prefault_disable; TUNABLE_INT("drm.i915.prefault_disable", &i915_prefault_disable); int i915_semaphores = -1; TUNABLE_INT("drm.i915.semaphores", &i915_semaphores); static int i915_try_reset = 1; TUNABLE_INT("drm.i915.try_reset", &i915_try_reset); unsigned int i915_lvds_downclock = 0; TUNABLE_INT("drm.i915.lvds_downclock", &i915_lvds_downclock); int i915_vbt_sdvo_panel_type = -1; TUNABLE_INT("drm.i915.vbt_sdvo_panel_type", &i915_vbt_sdvo_panel_type); unsigned int i915_powersave = 1; TUNABLE_INT("drm.i915.powersave", &i915_powersave); int i915_enable_fbc = 0; TUNABLE_INT("drm.i915.enable_fbc", &i915_enable_fbc); int i915_enable_rc6 = 0; TUNABLE_INT("drm.i915.enable_rc6", &i915_enable_rc6); int i915_lvds_channel_mode; TUNABLE_INT("drm.i915.lvds_channel_mode", &i915_lvds_channel_mode); int i915_panel_use_ssc = -1; TUNABLE_INT("drm.i915.panel_use_ssc", &i915_panel_use_ssc); int i915_panel_ignore_lid = 0; TUNABLE_INT("drm.i915.panel_ignore_lid", &i915_panel_ignore_lid); int i915_panel_invert_brightness; TUNABLE_INT("drm.i915.panel_invert_brightness", &i915_panel_invert_brightness); int i915_modeset = 1; TUNABLE_INT("drm.i915.modeset", &i915_modeset); int i915_enable_ppgtt = -1; TUNABLE_INT("drm.i915.enable_ppgtt", &i915_enable_ppgtt); int i915_enable_hangcheck = 1; TUNABLE_INT("drm.i915.enable_hangcheck", &i915_enable_hangcheck); +TUNABLE_INT("drm.i915.enable_unsupported", &i915_enable_unsupported); #define PCI_VENDOR_INTEL 0x8086 #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00 #define INTEL_PCH_LPT_DEVICE_ID_TYPE 0x8c00 void intel_detect_pch(struct drm_device *dev) { struct drm_i915_private *dev_priv; device_t pch; uint32_t id; dev_priv = dev->dev_private; pch = pci_find_class(PCIC_BRIDGE, PCIS_BRIDGE_ISA); if (pch != NULL && pci_get_vendor(pch) == PCI_VENDOR_INTEL) { id = pci_get_device(pch) & INTEL_PCH_DEVICE_ID_MASK; if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; dev_priv->num_pch_pll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); } else DRM_DEBUG_KMS("No PCH detected\n"); KASSERT(dev_priv->num_pch_pll <= I915_NUM_PLLS, ("num_pch_pll %d\n", dev_priv->num_pch_pll)); } else DRM_DEBUG_KMS("No Intel PCI-ISA bridge found\n"); } bool i915_semaphore_is_enabled(struct drm_device *dev) { if (INTEL_INFO(dev)->gen < 6) return 0; if (i915_semaphores >= 0) return i915_semaphores; /* Enable semaphores on SNB when IO remapping is off */ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) return false; return 1; } void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { int count; count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1)) DELAY(10); I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(FORCEWAKE); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0) DELAY(10); } void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) { int count; count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1)) DELAY(10); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1) == 0) DELAY(10); } void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { mtx_lock(&dev_priv->gt_lock); if (dev_priv->forcewake_count++ == 0) dev_priv->display.force_wake_get(dev_priv); mtx_unlock(&dev_priv->gt_lock); } static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv) { u32 gtfifodbg; gtfifodbg = I915_READ_NOTRACE(GTFIFODBG); if ((gtfifodbg & GT_FIFO_CPU_ERROR_MASK) != 0) { printf("MMIO read or write has been dropped %x\n", gtfifodbg); I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK); } } void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE, 0); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1)); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { mtx_lock(&dev_priv->gt_lock); if (--dev_priv->forcewake_count == 0) dev_priv->display.force_wake_put(dev_priv); mtx_unlock(&dev_priv->gt_lock); } int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) { int ret = 0; if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) { int loop = 500; u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { DELAY(10); fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); } if (loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES) { printf("%s loop\n", __func__); ++ret; } dev_priv->gt_fifo_count = fifo; } dev_priv->gt_fifo_count--; return (ret); } void vlv_force_wake_get(struct drm_i915_private *dev_priv) { int count; count = 0; /* Already awake? */ if ((I915_READ(0x130094) & 0xa1) == 0xa1) return; I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffffffff); POSTING_READ(FORCEWAKE_VLV); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0) DELAY(10); } void vlv_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffff0000); /* FIXME: confirm VLV behavior with Punit folks */ POSTING_READ(FORCEWAKE_VLV); } static int i8xx_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int onems; if (IS_I85X(dev)) return -ENODEV; onems = hz / 1000; if (onems == 0) onems = 1; I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830); POSTING_READ(D_STATE); if (IS_I830(dev) || IS_845G(dev)) { I915_WRITE(DEBUG_RESET_I830, DEBUG_RESET_DISPLAY | DEBUG_RESET_RENDER | DEBUG_RESET_FULL); POSTING_READ(DEBUG_RESET_I830); pause("i8xxrst1", onems); I915_WRITE(DEBUG_RESET_I830, 0); POSTING_READ(DEBUG_RESET_I830); } pause("i8xxrst2", onems); I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830); POSTING_READ(D_STATE); return 0; } static int i965_reset_complete(struct drm_device *dev) { u8 gdrst; gdrst = pci_read_config(dev->device, I965_GDRST, 1); return (gdrst & GRDOM_RESET_ENABLE) == 0; } static int i965_do_reset(struct drm_device *dev) { int ret; u8 gdrst; /* * Set the domains we want to reset (GRDOM/bits 2 and 3) as * well as the reset bit (GR/bit 0). Setting the GR bit * triggers the reset; when done, the hardware will clear it. */ gdrst = pci_read_config(dev->device, I965_GDRST, 1); pci_write_config(dev->device, I965_GDRST, gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE, 1); ret = wait_for(i965_reset_complete(dev), 500); if (ret) return ret; /* We can't reset render&media without also resetting display ... */ gdrst = pci_read_config(dev->device, I965_GDRST, 1); pci_write_config(dev->device, I965_GDRST, gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE, 1); return wait_for(i965_reset_complete(dev), 500); } static int ironlake_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv; u32 gdrst; int ret; dev_priv = dev->dev_private; gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE); ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); if (ret) return ret; /* We can't reset render&media without also resetting display ... */ gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } static int gen6_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv; int ret; dev_priv = dev->dev_private; /* Hold gt_lock across reset to prevent any register access * with forcewake not set correctly */ mtx_lock(&dev_priv->gt_lock); /* Reset the chip */ /* GEN6_GDRST is not in the gt power well, no need to check * for fifo space for the write or forcewake the chip for * the read */ I915_WRITE_NOTRACE(GEN6_GDRST, GEN6_GRDOM_FULL); /* Spin waiting for the device to ack the reset request */ ret = _intel_wait_for(dev, (I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500, 0, "915rst"); /* If reset with a user forcewake, try to restore, otherwise turn it off */ if (dev_priv->forcewake_count) dev_priv->display.force_wake_get(dev_priv); else dev_priv->display.force_wake_put(dev_priv); /* Restore fifo count */ dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); mtx_unlock(&dev_priv->gt_lock); return (ret); } int intel_gpu_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret = -ENODEV; switch (INTEL_INFO(dev)->gen) { case 7: case 6: ret = gen6_do_reset(dev); break; case 5: ret = ironlake_do_reset(dev); break; case 4: ret = i965_do_reset(dev); break; case 2: ret = i8xx_do_reset(dev); break; } /* Also reset the gpu hangman. */ if (dev_priv->stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); dev_priv->stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); ret = 0; } } return ret; } int i915_reset(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int ret; if (!i915_try_reset) return (0); if (!sx_try_xlock(&dev->dev_struct_lock)) return (-EBUSY); dev_priv->stop_rings = 0; i915_gem_reset(dev); ret = -ENODEV; if (time_second - dev_priv->last_gpu_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); dev_priv->last_gpu_reset = time_second; if (ret) { DRM_ERROR("Failed to reset chip.\n"); DRM_UNLOCK(dev); return (ret); } if (drm_core_check_feature(dev, DRIVER_MODESET) || !dev_priv->mm.suspended) { struct intel_ring_buffer *ring; int i; dev_priv->mm.suspended = 0; i915_gem_init_swizzling(dev); for_each_ring(ring, dev_priv, i) ring->init(ring); i915_gem_context_init(dev); i915_gem_init_ppgtt(dev); DRM_UNLOCK(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) intel_modeset_init_hw(dev); DRM_LOCK(dev); drm_irq_uninstall(dev); DRM_UNLOCK(dev); drm_irq_install(dev); } else DRM_UNLOCK(dev); return (0); } /* We give fast paths for the really cool registers */ #define NEEDS_FORCE_WAKE(dev_priv, reg) \ (((dev_priv)->info->gen >= 6) && \ ((reg) < 0x40000) && \ ((reg) != FORCEWAKE)) && \ (!IS_VALLEYVIEW((dev_priv)->dev)) #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x val = 0; \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ mtx_lock(&dev_priv->gt_lock); \ if (dev_priv->forcewake_count == 0) \ dev_priv->display.force_wake_get(dev_priv); \ val = DRM_READ##y(dev_priv->mmio_map, reg); \ if (dev_priv->forcewake_count == 0) \ dev_priv->display.force_wake_put(dev_priv); \ mtx_unlock(&dev_priv->gt_lock); \ } else { \ val = DRM_READ##y(dev_priv->mmio_map, reg); \ } \ trace_i915_reg_rw(false, reg, val, sizeof(val)); \ return val; \ } __i915_read(8, 8) __i915_read(16, 16) __i915_read(32, 32) __i915_read(64, 64) #undef __i915_read #define __i915_write(x, y) \ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ u32 __fifo_ret = 0; \ trace_i915_reg_rw(true, reg, val, sizeof(val)); \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \ } \ DRM_WRITE##y(dev_priv->mmio_map, reg, val); \ if (__predict_false(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ } __i915_write(8, 8) __i915_write(16, 16) __i915_write(32, 32) __i915_write(64, 64) #undef __i915_write Index: projects/building-blocks/sys/dev/drm2/i915/i915_drv.h =================================================================== --- projects/building-blocks/sys/dev/drm2/i915/i915_drv.h (revision 278195) +++ projects/building-blocks/sys/dev/drm2/i915/i915_drv.h (revision 278196) @@ -1,1574 +1,1574 @@ /* i915_drv.h -- Private header for the I915 driver -*- linux-c -*- */ /* * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, 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 (including the * next paragraph) 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 NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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 __FBSDID("$FreeBSD$"); #ifndef _I915_DRV_H_ #define _I915_DRV_H_ #include #include #include #include #include /* General customization: */ #define DRIVER_AUTHOR "Tungsten Graphics, Inc." #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" #define DRIVER_DATE "20080730" MALLOC_DECLARE(DRM_I915_GEM); enum pipe { PIPE_A = 0, PIPE_B, PIPE_C, I915_MAX_PIPES }; #define pipe_name(p) ((p) + 'A') #define I915_NUM_PIPE 2 enum plane { PLANE_A = 0, PLANE_B, PLANE_C, }; #define plane_name(p) ((p) + 'A') enum port { PORT_A = 0, PORT_B, PORT_C, PORT_D, PORT_E, I915_MAX_PORTS }; #define port_name(p) ((p) + 'A') #define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) struct intel_pch_pll { int refcount; /* count of number of CRTCs sharing this PLL */ int active; /* count of number of active CRTCs (i.e. DPMS on) */ bool on; /* is the PLL actually active? Disabled during modeset */ int pll_reg; int fp0_reg; int fp1_reg; }; #define I915_NUM_PLLS 2 /* Interface history: * * 1.1: Original. * 1.2: Add Power Management * 1.3: Add vblank support * 1.4: Fix cmdbuffer path, add heap destroy * 1.5: Add vblank pipe configuration * 1.6: - New ioctl for scheduling buffer swaps on vertical blank * - Support vertical blank on secondary display pipe */ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 #define WATCH_COHERENCY 0 #define WATCH_BUF 0 #define WATCH_EXEC 0 #define WATCH_LRU 0 #define WATCH_RELOC 0 #define WATCH_INACTIVE 0 #define WATCH_PWRITE 0 #define I915_GEM_PHYS_CURSOR_0 1 #define I915_GEM_PHYS_CURSOR_1 2 #define I915_GEM_PHYS_OVERLAY_REGS 3 #define I915_MAX_PHYS_OBJECT (I915_GEM_PHYS_OVERLAY_REGS) struct drm_i915_gem_phys_object { int id; drm_dma_handle_t *handle; struct drm_i915_gem_object *cur_obj; }; struct drm_i915_private; struct drm_i915_display_funcs { void (*dpms)(struct drm_crtc *crtc, int mode); bool (*fbc_enabled)(struct drm_device *dev); void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); void (*disable_fbc)(struct drm_device *dev); int (*get_display_clock_speed)(struct drm_device *dev); int (*get_fifo_size)(struct drm_device *dev, int plane); void (*update_wm)(struct drm_device *dev); void (*update_sprite_wm)(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); void (*sanitize_pm)(struct drm_device *dev); void (*update_linetime_wm)(struct drm_device *dev, int pipe, struct drm_display_mode *mode); int (*crtc_mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); void (*off)(struct drm_crtc *crtc); void (*write_eld)(struct drm_connector *connector, struct drm_crtc *crtc); void (*fdi_link_train)(struct drm_crtc *crtc); void (*init_clock_gating)(struct drm_device *dev); void (*init_pch_clock_gating)(struct drm_device *dev); int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj); void (*force_wake_get)(struct drm_i915_private *dev_priv); void (*force_wake_put)(struct drm_i915_private *dev_priv); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ /* display clock increase/decrease */ /* pll clock increase/decrease */ }; struct intel_device_info { u8 gen; + u8 not_supported:1; u8 is_mobile:1; u8 is_i85x:1; u8 is_i915g:1; u8 is_i945gm:1; u8 is_g33:1; u8 need_gfx_hws:1; u8 is_g4x:1; u8 is_pineview:1; u8 is_broadwater:1; u8 is_crestline:1; u8 is_ivybridge:1; u8 is_valleyview:1; u8 has_pch_split:1; u8 is_haswell:1; u8 has_fbc:1; u8 has_pipe_cxsr:1; u8 has_hotplug:1; u8 cursor_needs_physical:1; u8 has_overlay:1; u8 overlay_needs_physical:1; u8 supports_tv:1; u8 has_bsd_ring:1; u8 has_blt_ring:1; u8 has_llc:1; }; #define I915_PPGTT_PD_ENTRIES 512 #define I915_PPGTT_PT_ENTRIES 1024 struct i915_hw_ppgtt { unsigned num_pd_entries; vm_page_t *pt_pages; uint32_t pd_offset; vm_paddr_t *pt_dma_addr; vm_paddr_t scratch_page_dma_addr; }; /* This must match up with the value previously used for execbuf2.rsvd1. */ #define DEFAULT_CONTEXT_ID 0 struct i915_hw_context { uint32_t id; bool is_initialized; struct drm_i915_file_private *file_priv; struct intel_ring_buffer *ring; struct drm_i915_gem_object *obj; }; enum no_fbc_reason { FBC_NO_OUTPUT, /* no outputs enabled to compress */ FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */ FBC_UNSUPPORTED_MODE, /* interlace or doublescanned mode */ FBC_MODE_TOO_LARGE, /* mode too large for compression */ FBC_BAD_PLANE, /* fbc not supported on plane */ FBC_NOT_TILED, /* buffer not tiled */ FBC_MULTIPLE_PIPES, /* more than one pipe active */ FBC_MODULE_PARAM, }; struct mem_block { struct mem_block *next; struct mem_block *prev; int start; int size; struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */ }; struct opregion_header; struct opregion_acpi; struct opregion_swsci; struct opregion_asle; struct intel_opregion { struct opregion_header *header; struct opregion_acpi *acpi; struct opregion_swsci *swsci; struct opregion_asle *asle; void *vbt; u32 *lid_state; }; #define OPREGION_SIZE (8*1024) #define I915_FENCE_REG_NONE -1 #define I915_MAX_NUM_FENCES 16 /* 16 fences + sign bit for FENCE_REG_NONE */ #define I915_MAX_NUM_FENCE_BITS 5 struct drm_i915_fence_reg { struct list_head lru_list; struct drm_i915_gem_object *obj; int pin_count; }; struct sdvo_device_mapping { u8 initialized; u8 dvo_port; u8 slave_addr; u8 dvo_wiring; u8 i2c_pin; u8 ddc_pin; }; enum intel_pch { PCH_IBX, /* Ibexpeak PCH */ PCH_CPT, /* Cougarpoint PCH */ PCH_LPT, /* Lynxpoint PCH */ }; #define QUIRK_PIPEA_FORCE (1<<0) #define QUIRK_LVDS_SSC_DISABLE (1<<1) #define QUIRK_INVERT_BRIGHTNESS (1<<2) struct intel_fbdev; struct intel_fbc_work; typedef struct drm_i915_private { struct drm_device *dev; device_t gmbus_bridge[GMBUS_NUM_PORTS + 1]; device_t bbbus_bridge[GMBUS_NUM_PORTS + 1]; device_t gmbus[GMBUS_NUM_PORTS + 1]; device_t bbbus[GMBUS_NUM_PORTS + 1]; /** gmbus_sx protects against concurrent usage of the single hw gmbus * controller on different i2c buses. */ struct sx gmbus_sx; uint32_t gpio_mmio_base; int relative_constants_mode; drm_local_map_t *sarea; drm_local_map_t *mmio_map; /** gt_fifo_count and the subsequent register write are synchronized * with dev->struct_mutex. */ unsigned gt_fifo_count; /** forcewake_count is protected by gt_lock */ unsigned forcewake_count; /** gt_lock is also taken in irq contexts. */ struct mtx gt_lock; drm_i915_sarea_t *sarea_priv; /* drm_i915_ring_buffer_t ring; */ struct intel_ring_buffer rings[I915_NUM_RINGS]; uint32_t next_seqno; drm_dma_handle_t *status_page_dmah; void *hw_status_page; dma_addr_t dma_status_page; uint32_t counter; unsigned int status_gfx_addr; struct drm_gem_object *hws_obj; struct drm_i915_gem_object *pwrctx; struct drm_i915_gem_object *renderctx; unsigned int cpp; int back_offset; int front_offset; int current_page; int page_flipping; atomic_t irq_received; u32 trace_irq_seqno; /** Cached value of IER to avoid reads in updating the bitfield */ u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; u32 pch_irq_mask; struct mtx irq_lock; struct mtx dpio_lock; u32 hotplug_supported_mask; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int num_pipe; int num_pch_pll; /* For hangcheck timer */ #define DRM_I915_HANGCHECK_PERIOD ((1500 /* in ms */ * hz) / 1000) int hangcheck_count; uint32_t last_acthd[I915_NUM_RINGS]; uint32_t last_instdone; uint32_t last_instdone1; unsigned int stop_rings; struct intel_opregion opregion; /* overlay */ struct intel_overlay *overlay; bool sprite_scaling_enabled; /* LVDS info */ int backlight_level; /* restore backlight to this value */ bool backlight_enabled; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ /* Feature bits from the VBIOS */ unsigned int int_tv_support:1; unsigned int lvds_dither:1; unsigned int lvds_vbt:1; unsigned int int_crt_support:1; unsigned int lvds_use_ssc:1; unsigned int display_clock_mode:1; int lvds_ssc_freq; unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ unsigned int lvds_val; /* used for checking LVDS channel mode */ struct { int rate; int lanes; int preemphasis; int vswing; bool initialized; bool support; int bpp; struct edp_power_seq pps; } edp; bool no_aux_handshake; int crt_ddc_pin; struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ /* PCH chipset type */ enum intel_pch pch_type; /* Display functions */ struct drm_i915_display_funcs display; unsigned long quirks; /* Register state */ bool modeset_on_lid; u8 saveLBB; u32 saveDSPACNTR; u32 saveDSPBCNTR; u32 saveDSPARB; u32 saveHWS; u32 savePIPEACONF; u32 savePIPEBCONF; u32 savePIPEASRC; u32 savePIPEBSRC; u32 saveFPA0; u32 saveFPA1; u32 saveDPLL_A; u32 saveDPLL_A_MD; u32 saveHTOTAL_A; u32 saveHBLANK_A; u32 saveHSYNC_A; u32 saveVTOTAL_A; u32 saveVBLANK_A; u32 saveVSYNC_A; u32 saveBCLRPAT_A; u32 saveTRANSACONF; u32 saveTRANS_HTOTAL_A; u32 saveTRANS_HBLANK_A; u32 saveTRANS_HSYNC_A; u32 saveTRANS_VTOTAL_A; u32 saveTRANS_VBLANK_A; u32 saveTRANS_VSYNC_A; u32 savePIPEASTAT; u32 saveDSPASTRIDE; u32 saveDSPASIZE; u32 saveDSPAPOS; u32 saveDSPAADDR; u32 saveDSPASURF; u32 saveDSPATILEOFF; u32 savePFIT_PGM_RATIOS; u32 saveBLC_HIST_CTL; u32 saveBLC_PWM_CTL; u32 saveBLC_PWM_CTL2; u32 saveBLC_CPU_PWM_CTL; u32 saveBLC_CPU_PWM_CTL2; u32 saveFPB0; u32 saveFPB1; u32 saveDPLL_B; u32 saveDPLL_B_MD; u32 saveHTOTAL_B; u32 saveHBLANK_B; u32 saveHSYNC_B; u32 saveVTOTAL_B; u32 saveVBLANK_B; u32 saveVSYNC_B; u32 saveBCLRPAT_B; u32 saveTRANSBCONF; u32 saveTRANS_HTOTAL_B; u32 saveTRANS_HBLANK_B; u32 saveTRANS_HSYNC_B; u32 saveTRANS_VTOTAL_B; u32 saveTRANS_VBLANK_B; u32 saveTRANS_VSYNC_B; u32 savePIPEBSTAT; u32 saveDSPBSTRIDE; u32 saveDSPBSIZE; u32 saveDSPBPOS; u32 saveDSPBADDR; u32 saveDSPBSURF; u32 saveDSPBTILEOFF; u32 saveVGA0; u32 saveVGA1; u32 saveVGA_PD; u32 saveVGACNTRL; u32 saveADPA; u32 saveLVDS; u32 savePP_ON_DELAYS; u32 savePP_OFF_DELAYS; u32 saveDVOA; u32 saveDVOB; u32 saveDVOC; u32 savePP_ON; u32 savePP_OFF; u32 savePP_CONTROL; u32 savePP_DIVISOR; u32 savePFIT_CONTROL; u32 save_palette_a[256]; u32 save_palette_b[256]; u32 saveDPFC_CB_BASE; u32 saveFBC_CFB_BASE; u32 saveFBC_LL_BASE; u32 saveFBC_CONTROL; u32 saveFBC_CONTROL2; u32 saveIER; u32 saveIIR; u32 saveIMR; u32 saveDEIER; u32 saveDEIMR; u32 saveGTIER; u32 saveGTIMR; u32 saveFDI_RXA_IMR; u32 saveFDI_RXB_IMR; u32 saveCACHE_MODE_0; u32 saveMI_ARB_STATE; u32 saveSWF0[16]; u32 saveSWF1[16]; u32 saveSWF2[3]; u8 saveMSR; u8 saveSR[8]; u8 saveGR[25]; u8 saveAR_INDEX; u8 saveAR[21]; u8 saveDACMASK; u8 saveCR[37]; uint64_t saveFENCE[I915_MAX_NUM_FENCES]; u32 saveCURACNTR; u32 saveCURAPOS; u32 saveCURABASE; u32 saveCURBCNTR; u32 saveCURBPOS; u32 saveCURBBASE; u32 saveCURSIZE; u32 saveDP_B; u32 saveDP_C; u32 saveDP_D; u32 savePIPEA_GMCH_DATA_M; u32 savePIPEB_GMCH_DATA_M; u32 savePIPEA_GMCH_DATA_N; u32 savePIPEB_GMCH_DATA_N; u32 savePIPEA_DP_LINK_M; u32 savePIPEB_DP_LINK_M; u32 savePIPEA_DP_LINK_N; u32 savePIPEB_DP_LINK_N; u32 saveFDI_RXA_CTL; u32 saveFDI_TXA_CTL; u32 saveFDI_RXB_CTL; u32 saveFDI_TXB_CTL; u32 savePFA_CTL_1; u32 savePFB_CTL_1; u32 savePFA_WIN_SZ; u32 savePFB_WIN_SZ; u32 savePFA_WIN_POS; u32 savePFB_WIN_POS; u32 savePCH_DREF_CONTROL; u32 saveDISP_ARB_CTL; u32 savePIPEA_DATA_M1; u32 savePIPEA_DATA_N1; u32 savePIPEA_LINK_M1; u32 savePIPEA_LINK_N1; u32 savePIPEB_DATA_M1; u32 savePIPEB_DATA_N1; u32 savePIPEB_LINK_M1; u32 savePIPEB_LINK_N1; u32 saveMCHBAR_RENDER_STANDBY; u32 savePCH_PORT_HOTPLUG; struct { /** Memory allocator for GTT stolen memory */ struct drm_mm stolen; /** Memory allocator for GTT */ struct drm_mm gtt_space; /** List of all objects in gtt_space. Used to restore gtt * mappings on resume */ struct list_head gtt_list; /** Usable portion of the GTT for GEM */ unsigned long gtt_start; unsigned long gtt_mappable_end; unsigned long gtt_end; /** PPGTT used for aliasing the PPGTT with the GTT */ struct i915_hw_ppgtt *aliasing_ppgtt; /** * List of objects currently involved in rendering from the * ringbuffer. * * Includes buffers having the contents of their GPU caches * flushed, not necessarily primitives. last_rendering_seqno * represents when the rendering involved will be completed. * * A reference is held on the buffer while on this list. */ struct list_head active_list; /** * List of objects which are not in the ringbuffer but which * still have a write_domain which needs to be flushed before * unbinding. * * A reference is held on the buffer while on this list. */ struct list_head flushing_list; /** * LRU list of objects which are not in the ringbuffer and * are ready to unbind, but are still in the GTT. * * last_rendering_seqno is 0 while an object is in this list. * * A reference is not held on the buffer while on this list, * as merely being GTT-bound shouldn't prevent its being * freed, and we'll pull it off the list in the free path. */ struct list_head inactive_list; /** LRU list of objects with fence regs on them. */ struct list_head fence_list; /** * We leave the user IRQ off as much as possible, * but this means that requests will finish and never * be retired once the system goes idle. Set a timer to * fire periodically while the ring is running. When it * fires, go retire requests. */ struct timeout_task retire_task; /** * Are we in a non-interruptible section of code like * modesetting? */ bool interruptible; uint32_t next_gem_seqno; /** * Waiting sequence number, if any */ uint32_t waiting_gem_seqno; /** * Last seq seen at irq time */ uint32_t irq_gem_seqno; /** * Flag if the X Server, and thus DRM, is not currently in * control of the device. * * This is set between LeaveVT and EnterVT. It needs to be * replaced with a semaphore. It also needs to be * transitioned away from for kernel modesetting. */ int suspended; /** * Flag if the hardware appears to be wedged. * * This is set when attempts to idle the device timeout. * It prevents command submission from occuring and makes * every pending request fail */ int wedged; /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; /** Bit 6 swizzling required for Y tiling */ uint32_t bit_6_swizzle_y; /* storage for physical objects */ struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; /* accounting, useful for userland debugging */ size_t gtt_total; size_t mappable_gtt_total; size_t object_memory; u32 object_count; struct intel_gtt gtt; eventhandler_tag i915_lowmem; } mm; const struct intel_device_info *info; /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct { unsigned allow_batchbuffer : 1; u32 *gfx_hws_cpu_addr; } dri1; /* Kernel Modesetting */ struct sdvo_device_mapping sdvo_mappings[2]; /* indicate whether the LVDS_BORDER should be enabled or not */ unsigned int lvds_border_bits; /* Panel fitter placement and size for Ironlake+ */ u32 pch_pf_pos, pch_pf_size; struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; /* wait_queue_head_t pending_flip_queue; XXXKIB */ struct intel_pch_pll pch_plls[I915_NUM_PLLS]; /* Reclocking support */ bool render_reclock_avail; bool lvds_downclock_avail; /* indicates the reduced downclock for LVDS*/ int lvds_downclock; struct task idle_task; struct callout idle_callout; bool busy; u16 orig_clock; int child_dev_num; struct child_device_config *child_dev; struct drm_connector *int_lvds_connector; struct drm_connector *int_edp_connector; device_t bridge_dev; bool mchbar_need_disable; int mch_res_rid; struct resource *mch_res; struct mtx rps_lock; u32 pm_iir; struct task rps_task; u8 cur_delay; u8 min_delay; u8 max_delay; u8 fmax; u8 fstart; u64 last_count1; unsigned long last_time1; unsigned long chipset_power; u64 last_count2; struct timespec last_time2; unsigned long gfx_power; int c_m; int r_t; u8 corr; struct mtx *mchdev_lock; enum no_fbc_reason no_fbc_reason; struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; unsigned long cfb_size; unsigned int cfb_fb; int cfb_plane; int cfb_y; struct intel_fbc_work *fbc_work; unsigned int fsb_freq, mem_freq, is_ddr3; struct taskqueue *tq; struct task error_task; struct task hotplug_task; int error_completion; struct mtx error_completion_lock; /* Protected by dev->error_lock. */ struct drm_i915_error_state *first_error; struct mtx error_lock; struct callout hangcheck_timer; unsigned long last_gpu_reset; struct intel_fbdev *fbdev; struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; bool hw_contexts_disabled; uint32_t hw_context_size; } drm_i915_private_t; /* Iterate over initialised rings */ #define for_each_ring(ring__, dev_priv__, i__) \ for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ if (((ring__) = &(dev_priv__)->rings[(i__)]), intel_ring_initialized((ring__))) enum hdmi_force_audio { HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */ HDMI_AUDIO_OFF, /* force turn off HDMI audio */ HDMI_AUDIO_AUTO, /* trust EDID */ HDMI_AUDIO_ON, /* force turn on HDMI audio */ }; enum i915_cache_level { I915_CACHE_NONE, I915_CACHE_LLC, I915_CACHE_LLC_MLC, /* gen6+ */ }; enum intel_chip_family { CHIP_I8XX = 0x01, CHIP_I9XX = 0x02, CHIP_I915 = 0x04, CHIP_I965 = 0x08, }; /** driver private structure attached to each drm_gem_object */ struct drm_i915_gem_object { struct drm_gem_object base; /** Current space allocated to this object in the GTT, if any. */ struct drm_mm_node *gtt_space; struct list_head gtt_list; /** This object's place on the active/flushing/inactive lists */ struct list_head ring_list; struct list_head mm_list; /** This object's place on GPU write list */ struct list_head gpu_write_list; /** This object's place in the batchbuffer or on the eviction list */ struct list_head exec_list; /** * This is set if the object is on the active or flushing lists * (has pending rendering), and is not set if it's on inactive (ready * to be unbound). */ unsigned int active:1; /** * This is set if the object has been written to since last bound * to the GTT */ unsigned int dirty:1; /** * This is set if the object has been written to since the last * GPU flush. */ unsigned int pending_gpu_write:1; /** * Fence register bits (if any) for this object. Will be set * as needed when mapped into the GTT. * Protected by dev->struct_mutex. */ signed int fence_reg:I915_MAX_NUM_FENCE_BITS; /** * Advice: are the backing pages purgeable? */ unsigned int madv:2; /** * Current tiling mode for the object. */ unsigned int tiling_mode:2; /** * Whether the tiling parameters for the currently associated fence * register have changed. Note that for the purposes of tracking * tiling changes we also treat the unfenced register, the register * slot that the object occupies whilst it executes a fenced * command (such as BLT on gen2/3), as a "fence". */ unsigned int fence_dirty:1; /** How many users have pinned this object in GTT space. The following * users can each hold at most one reference: pwrite/pread, pin_ioctl * (via user_pin_count), execbuffer (objects are not allowed multiple * times for the same batchbuffer), and the framebuffer code. When * switching/pageflipping, the framebuffer code has at most two buffers * pinned per crtc. * * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3 * bits with absolutely no headroom. So use 4 bits. */ unsigned int pin_count:4; #define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf /** * Is the object at the current location in the gtt mappable and * fenceable? Used to avoid costly recalculations. */ unsigned int map_and_fenceable:1; /** * Whether the current gtt mapping needs to be mappable (and isn't just * mappable by accident). Track pin and fault separate for a more * accurate mappable working set. */ unsigned int fault_mappable:1; unsigned int pin_mappable:1; unsigned int pin_display:1; /* * Is the GPU currently using a fence to access this buffer, */ unsigned int pending_fenced_gpu_access:1; unsigned int fenced_gpu_access:1; unsigned int cache_level:2; unsigned int has_aliasing_ppgtt_mapping:1; unsigned int has_global_gtt_mapping:1; vm_page_t *pages; int pages_pin_count; /** * DMAR support */ struct sglist *sg_list; /** * Used for performing relocations during execbuffer insertion. */ LIST_ENTRY(drm_i915_gem_object) exec_node; unsigned long exec_handle; struct drm_i915_gem_exec_object2 *exec_entry; /** * Current offset of the object in GTT space. * * This is the same as gtt_space->start */ uint32_t gtt_offset; struct intel_ring_buffer *ring; /** Breadcrumb of last rendering to the buffer. */ uint32_t last_rendering_seqno; /** Breadcrumb of last fenced GPU access to the buffer. */ uint32_t last_fenced_seqno; /** Current tiling stride for the object, if it's tiled. */ uint32_t stride; /** Record of address bit 17 of each page at last unbind. */ unsigned long *bit_17; /** User space pin count and filp owning the pin */ uint32_t user_pin_count; struct drm_file *pin_filp; /** for phy allocated objects */ struct drm_i915_gem_phys_object *phys_obj; /** * Number of crtcs where this object is currently the fb, but * will be page flipped away on the next vblank. When it * reaches 0, dev_priv->pending_flip_queue will be woken up. */ int pending_flip; }; #define to_intel_bo(x) __containerof(x, struct drm_i915_gem_object, base) /** * Request queue structure. * * The request queue allows us to note sequence numbers that have been emitted * and may be associated with active buffers to be retired. * * By keeping this list, we can avoid having to do questionable * sequence-number comparisons on buffer last_rendering_seqnos, and associate * an emission time with seqnos for tracking how far ahead of the GPU we are. */ struct drm_i915_gem_request { /** On Which ring this request was generated */ struct intel_ring_buffer *ring; /** GEM sequence number associated with this request. */ uint32_t seqno; /** Postion in the ringbuffer of the end of the request */ u32 tail; /** Time at which this request was emitted, in jiffies. */ unsigned long emitted_jiffies; /** global list entry for this request */ struct list_head list; struct drm_i915_file_private *file_priv; /** file_priv list entry for this request */ struct list_head client_list; }; struct drm_i915_file_private { struct { struct list_head request_list; struct mtx lck; } mm; struct drm_gem_names context_idr; }; struct drm_i915_error_state { u_int ref; u32 eir; u32 pgtbl_er; u32 ier; bool waiting[I915_NUM_RINGS]; u32 pipestat[I915_MAX_PIPES]; u32 tail[I915_NUM_RINGS]; u32 head[I915_NUM_RINGS]; u32 ipeir[I915_NUM_RINGS]; u32 ipehr[I915_NUM_RINGS]; u32 instdone[I915_NUM_RINGS]; u32 acthd[I915_NUM_RINGS]; u32 semaphore_mboxes[I915_NUM_RINGS][I915_NUM_RINGS - 1]; /* our own tracking of ring head and tail */ u32 cpu_ring_head[I915_NUM_RINGS]; u32 cpu_ring_tail[I915_NUM_RINGS]; u32 error; /* gen6+ */ u32 instpm[I915_NUM_RINGS]; u32 instps[I915_NUM_RINGS]; u32 instdone1; u32 seqno[I915_NUM_RINGS]; u64 bbaddr; u32 fault_reg[I915_NUM_RINGS]; u32 done_reg; u32 faddr[I915_NUM_RINGS]; u64 fence[I915_MAX_NUM_FENCES]; struct timeval time; struct drm_i915_error_ring { struct drm_i915_error_object { int page_count; u32 gtt_offset; u32 *pages[0]; } *ringbuffer, *batchbuffer; struct drm_i915_error_request { long jiffies; u32 seqno; u32 tail; } *requests; int num_requests; } ring[I915_NUM_RINGS]; struct drm_i915_error_buffer { u32 size; u32 name; u32 seqno; u32 gtt_offset; u32 read_domains; u32 write_domain; s32 fence_reg:I915_MAX_NUM_FENCE_BITS; s32 pinned:2; u32 tiling:2; u32 dirty:1; u32 purgeable:1; s32 ring:4; u32 cache_level:2; } *active_bo, *pinned_bo; u32 active_bo_count, pinned_bo_count; struct intel_overlay_error_state *overlay; struct intel_display_error_state *display; }; /** * RC6 is a special power stage which allows the GPU to enter an very * low-voltage mode when idle, using down to 0V while at this stage. This * stage is entered automatically when the GPU is idle when RC6 support is * enabled, and as soon as new workload arises GPU wakes up automatically as well. * * There are different RC6 modes available in Intel GPU, which differentiate * among each other with the latency required to enter and leave RC6 and * voltage consumed by the GPU in different states. * * The combination of the following flags define which states GPU is allowed * to enter, while RC6 is the normal RC6 state, RC6p is the deep RC6, and * RC6pp is deepest RC6. Their support by hardware varies according to the * GPU, BIOS, chipset and platform. RC6 is usually the safest one and the one * which brings the most power savings; deeper states save more power, but * require higher latency to switch to and wake up. */ #define INTEL_RC6_ENABLE (1<<0) #define INTEL_RC6p_ENABLE (1<<1) #define INTEL_RC6pp_ENABLE (1<<2) extern int intel_iommu_enabled; extern struct drm_ioctl_desc i915_ioctls[]; extern struct drm_driver_info i915_driver_info; extern struct cdev_pager_ops i915_gem_pager_ops; extern unsigned int i915_fbpercrtc; extern int i915_panel_ignore_lid; extern int i915_panel_invert_brightness; extern unsigned int i915_powersave; extern int i915_prefault_disable; extern int i915_semaphores; extern unsigned int i915_lvds_downclock; extern int i915_lvds_channel_mode; extern int i915_panel_use_ssc; extern int i915_vbt_sdvo_panel_type; extern int i915_enable_rc6; extern int i915_enable_fbc; extern int i915_enable_ppgtt; extern int i915_enable_hangcheck; const struct intel_device_info *i915_get_device_id(int device); int i915_reset(struct drm_device *dev); extern int intel_gpu_reset(struct drm_device *dev); /* i915_debug.c */ int i915_sysctl_init(struct drm_device *dev, struct sysctl_ctx_list *ctx, struct sysctl_oid *top); void i915_sysctl_cleanup(struct drm_device *dev); /* i915_dma.c */ int i915_batchbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_update_dri1_breadcrumb(struct drm_device *dev); extern void i915_kernel_lost_context(struct drm_device * dev); extern int i915_driver_load(struct drm_device *, unsigned long flags); extern int i915_driver_unload(struct drm_device *); extern int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv); extern void i915_driver_lastclose(struct drm_device * dev); extern void i915_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); extern void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv); extern int i915_driver_device_is_agp(struct drm_device * dev); extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); extern int i915_emit_box(struct drm_device *dev, struct drm_clip_rect __user *boxes, int i, int DR1, int DR4); int i915_emit_box_p(struct drm_device *dev, struct drm_clip_rect *box, int DR1, int DR4); unsigned long i915_chipset_val(struct drm_i915_private *dev_priv); unsigned long i915_mch_val(struct drm_i915_private *dev_priv); void i915_update_gfx_val(struct drm_i915_private *dev_priv); unsigned long i915_gfx_val(struct drm_i915_private *dev_priv); unsigned long i915_read_mch_val(void); bool i915_gpu_raise(void); bool i915_gpu_lower(void); bool i915_gpu_busy(void); bool i915_gpu_turbo_disable(void); /* i915_irq.c */ extern int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void intel_irq_init(struct drm_device *dev); void intel_enable_asle(struct drm_device *dev); void i915_hangcheck_elapsed(void *context); void i915_handle_error(struct drm_device *dev, bool wedged); void i915_error_state_free(struct drm_i915_error_state *error); void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void i915_destroy_error_state(struct drm_device *dev); /* i915_gem.c */ int i915_gem_create(struct drm_file *file, struct drm_device *dev, uint64_t size, uint32_t *handle_p); int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_pread_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_execbuffer2(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_pin_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_unpin_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_throttle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_set_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); void i915_gem_unload(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_free_object(struct drm_gem_object *obj); int i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int i915_gem_object_unbind(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); static inline bool i915_gem_object_pin_fence(struct drm_i915_gem_object *obj) { if (obj->fence_reg != I915_FENCE_REG_NONE) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; dev_priv->fence_regs[obj->fence_reg].pin_count++; return true; } else return false; } static inline void i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) { if (obj->fence_reg != I915_FENCE_REG_NONE) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; dev_priv->fence_regs[obj->fence_reg].pin_count--; } } void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); uint32_t i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, uint32_t size, int tiling_mode); int i915_mutex_lock_interruptible(struct drm_device *dev); int i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write); int i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write); int i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, u32 alignment, struct intel_ring_buffer *pipelined); void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj); int i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); int i915_gem_flush_ring(struct intel_ring_buffer *ring, uint32_t invalidate_domains, uint32_t flush_domains); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); int i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj); int i915_gem_object_sync(struct drm_i915_gem_object *obj, struct intel_ring_buffer *to); int i915_gem_object_put_fence(struct drm_i915_gem_object *obj); int i915_gem_idle(struct drm_device *dev); int i915_gem_init(struct drm_device *dev); int i915_gem_init_hw(struct drm_device *dev); void i915_gem_init_swizzling(struct drm_device *dev); void i915_gem_init_ppgtt(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); int i915_gpu_idle(struct drm_device *dev); void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, struct intel_ring_buffer *ring, uint32_t seqno); int i915_add_request(struct intel_ring_buffer *ring, struct drm_file *file, struct drm_i915_gem_request *request); int i915_gem_object_get_fence(struct drm_i915_gem_object *obj); void i915_gem_reset(struct drm_device *dev); int i915_wait_request(struct intel_ring_buffer *ring, uint32_t seqno); int i915_gem_mmap(struct drm_device *dev, uint64_t offset, int prot); int i915_gem_fault(struct drm_device *dev, uint64_t offset, int prot, uint64_t *phys); void i915_gem_release(struct drm_device *dev, struct drm_file *file); int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); /* i915_gem_context.c */ void i915_gem_context_init(struct drm_device *dev); void i915_gem_context_fini(struct drm_device *dev); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct intel_ring_buffer *ring, struct drm_file *file, int to_id); int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_detach_phys_object(struct drm_device *dev, struct drm_i915_gem_object *obj); int i915_gem_attach_phys_object(struct drm_device *dev, struct drm_i915_gem_object *obj, int id, int align); int i915_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj); void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj); void i915_gem_object_do_bit_17_swizzle_page(struct drm_i915_gem_object *obj, struct vm_page *m); /* i915_gem_evict.c */ int i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, bool mappable); int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); /* i915_suspend.c */ extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); /* intel_iic.c */ extern int intel_setup_gmbus(struct drm_device *dev); extern void intel_teardown_gmbus(struct drm_device *dev); extern void intel_gmbus_set_speed(device_t idev, int speed); extern void intel_gmbus_force_bit(device_t idev, bool force_bit); extern void intel_iic_reset(struct drm_device *dev); static inline bool intel_gmbus_is_port_valid(unsigned port) { return (port >= GMBUS_PORT_SSC && port <= GMBUS_PORT_DPD); } extern device_t intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned port); /* intel_opregion.c */ int intel_opregion_setup(struct drm_device *dev); extern void intel_opregion_init(struct drm_device *dev); extern void intel_opregion_fini(struct drm_device *dev); extern void intel_opregion_asle_intr(struct drm_device *dev); extern void intel_opregion_gse_intr(struct drm_device *dev); extern void intel_opregion_enable_asle(struct drm_device *dev); /* i915_gem_gtt.c */ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev); void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev); void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt, struct drm_i915_gem_object *obj); void i915_gem_restore_gtt_mappings(struct drm_device *dev); int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj); int i915_gem_init_global_gtt(struct drm_device *dev, unsigned long start, unsigned long mappable_end, unsigned long end); /* modesetting */ extern void intel_modeset_init_hw(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); -extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void ironlake_init_pch_refclk(struct drm_device *dev); extern void ironlake_enable_rc6(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void intel_detect_pch(struct drm_device *dev); extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); /* IPS */ extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); extern bool i915_semaphore_is_enabled(struct drm_device *dev); extern void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv); extern void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv); extern void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv); extern void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv); extern void vlv_force_wake_get(struct drm_i915_private *dev_priv); extern void vlv_force_wake_put(struct drm_i915_private *dev_priv); extern struct intel_overlay_error_state *intel_overlay_capture_error_state( struct drm_device *dev); extern void intel_overlay_print_error_state(struct sbuf *m, struct intel_overlay_error_state *error); extern struct intel_display_error_state *intel_display_capture_error_state( struct drm_device *dev); extern void intel_display_print_error_state(struct sbuf *m, struct drm_device *dev, struct intel_display_error_state *error); static inline void trace_i915_reg_rw(boolean_t rw, int reg, uint64_t val, int sz) { CTR4(KTR_DRM_REG, "[%x/%d] %c %x", reg, sz, rw ? "w" : "r", val); } /* On SNB platform, before reading ring registers forcewake bit * must be set to prevent GT core from power down and stale values being * returned. */ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv); void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv); int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); __i915_read(8, 8) __i915_read(16, 16) __i915_read(32, 32) __i915_read(64, 64) #undef __i915_read #define __i915_write(x, y) \ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val); __i915_write(8, 8) __i915_write(16, 16) __i915_write(32, 32) __i915_write(64, 64) #undef __i915_write #define I915_READ8(reg) i915_read8(dev_priv, (reg)) #define I915_WRITE8(reg, val) i915_write8(dev_priv, (reg), (val)) #define I915_READ16(reg) i915_read16(dev_priv, (reg)) #define I915_WRITE16(reg, val) i915_write16(dev_priv, (reg), (val)) #define I915_READ16_NOTRACE(reg) DRM_READ16(dev_priv->mmio_map, (reg)) #define I915_WRITE16_NOTRACE(reg, val) DRM_WRITE16(dev_priv->mmio_map, (reg), (val)) #define I915_READ(reg) i915_read32(dev_priv, (reg)) #define I915_WRITE(reg, val) i915_write32(dev_priv, (reg), (val)) #define I915_READ_NOTRACE(reg) DRM_READ32(dev_priv->mmio_map, (reg)) #define I915_WRITE_NOTRACE(reg, val) DRM_WRITE32(dev_priv->mmio_map, (reg), (val)) #define I915_WRITE64(reg, val) i915_write64(dev_priv, (reg), (val)) #define I915_READ64(reg) i915_read64(dev_priv, (reg)) #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) #define I915_VERBOSE 0 /** * Reads a dword out of the status page, which is written to from the command * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or * MI_STORE_DATA_IMM. * * The following dwords have a reserved meaning: * 0x00: ISR copy, updated when an ISR bit not set in the HWSTAM changes. * 0x04: ring 0 head pointer * 0x05: ring 1 head pointer (915-class) * 0x06: ring 2 head pointer (915-class) * 0x10-0x1b: Context status DWords (GM45) * 0x1f: Last written status offset. (GM45) * * The area from dword 0x20 to 0x3ff is available for driver usage. */ #define I915_GEM_HWS_INDEX 0x20 #define INTEL_INFO(dev) (((struct drm_i915_private *) (dev)->dev_private)->info) #define IS_I830(dev) ((dev)->pci_device == 0x3577) #define IS_845G(dev) ((dev)->pci_device == 0x2562) #define IS_I85X(dev) (INTEL_INFO(dev)->is_i85x) #define IS_I865G(dev) ((dev)->pci_device == 0x2572) #define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g) #define IS_I915GM(dev) ((dev)->pci_device == 0x2592) #define IS_I945G(dev) ((dev)->pci_device == 0x2772) #define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm) #define IS_BROADWATER(dev) (INTEL_INFO(dev)->is_broadwater) #define IS_CRESTLINE(dev) (INTEL_INFO(dev)->is_crestline) #define IS_GM45(dev) ((dev)->pci_device == 0x2A42) #define IS_G4X(dev) (INTEL_INFO(dev)->is_g4x) #define IS_PINEVIEW_G(dev) ((dev)->pci_device == 0xa001) #define IS_PINEVIEW_M(dev) ((dev)->pci_device == 0xa011) #define IS_PINEVIEW(dev) (INTEL_INFO(dev)->is_pineview) #define IS_G33(dev) (INTEL_INFO(dev)->is_g33) #define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042) #define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046) #define IS_IVYBRIDGE(dev) (INTEL_INFO(dev)->is_ivybridge) #define IS_VALLEYVIEW(dev) (INTEL_INFO(dev)->is_valleyview) #define IS_HASWELL(dev) (INTEL_INFO(dev)->is_haswell) #define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile) /* XXXKIB LEGACY */ #define IS_I965G(dev) ((dev)->pci_device == 0x2972 || \ (dev)->pci_device == 0x2982 || \ (dev)->pci_device == 0x2992 || \ (dev)->pci_device == 0x29A2 || \ (dev)->pci_device == 0x2A02 || \ (dev)->pci_device == 0x2A12 || \ (dev)->pci_device == 0x2A42 || \ (dev)->pci_device == 0x2E02 || \ (dev)->pci_device == 0x2E12 || \ (dev)->pci_device == 0x2E22 || \ (dev)->pci_device == 0x2E32) #define IS_I965GM(dev) ((dev)->pci_device == 0x2A02) #define IS_IGDG(dev) ((dev)->pci_device == 0xa001) #define IS_IGDGM(dev) ((dev)->pci_device == 0xa011) #define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev)) #define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \ IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev)) /* XXXKIB LEGACY END */ #define IS_GEN2(dev) (INTEL_INFO(dev)->gen == 2) #define IS_GEN3(dev) (INTEL_INFO(dev)->gen == 3) #define IS_GEN4(dev) (INTEL_INFO(dev)->gen == 4) #define IS_GEN5(dev) (INTEL_INFO(dev)->gen == 5) #define IS_GEN6(dev) (INTEL_INFO(dev)->gen == 6) #define IS_GEN7(dev) (INTEL_INFO(dev)->gen == 7) #define HAS_BSD(dev) (INTEL_INFO(dev)->has_bsd_ring) #define HAS_BLT(dev) (INTEL_INFO(dev)->has_blt_ring) #define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc) #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) #define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) #define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >=6) #define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay) #define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical) /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte * rows, which changed the alignment requirements and fence programming. */ #define HAS_128_BYTE_Y_TILING(dev) (!IS_GEN2(dev) && !(IS_I915G(dev) || \ IS_I915GM(dev))) #define SUPPORTS_DIGITAL_OUTPUTS(dev) (!IS_GEN2(dev) && !IS_PINEVIEW(dev)) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_GEN5(dev)) #define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_GEN5(dev)) #define SUPPORTS_EDP(dev) (IS_IRONLAKE_M(dev)) #define SUPPORTS_TV(dev) (INTEL_INFO(dev)->supports_tv) #define I915_HAS_HOTPLUG(dev) (INTEL_INFO(dev)->has_hotplug) /* dsparb controlled by hw only */ #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev)) #define HAS_FW_BLC(dev) (INTEL_INFO(dev)->gen > 2) #define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr) #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc) #define HAS_PCH_SPLIT(dev) (INTEL_INFO(dev)->has_pch_split) #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) #define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type) #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT) #define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT) #define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX) #define PRIMARY_RINGBUFFER_SIZE (128*1024) static inline bool i915_seqno_passed(uint32_t seq1, uint32_t seq2) { return ((int32_t)(seq1 - seq2) >= 0); } static inline void i915_gem_chipset_flush(struct drm_device *dev) { if (INTEL_INFO(dev)->gen < 6) intel_gtt_chipset_flush(); } static inline void i915_gem_object_pin_pages(struct drm_i915_gem_object *obj) { /* KASSERT(obj->pages != NULL, ("pin and NULL pages")); */ obj->pages_pin_count++; } static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) { KASSERT(obj->pages_pin_count != 0, ("zero pages_pin_count")); obj->pages_pin_count--; } u32 i915_gem_next_request_seqno(struct intel_ring_buffer *ring); #endif Index: projects/building-blocks/sys/dev/drm2/i915/intel_dp.c =================================================================== --- projects/building-blocks/sys/dev/drm2/i915/intel_dp.c (revision 278195) +++ projects/building-blocks/sys/dev/drm2/i915/intel_dp.c (revision 278196) @@ -1,2593 +1,2593 @@ /* * Copyright © 2008 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (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 (including the next * paragraph) 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. * * Authors: * Keith Packard * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #define DP_RECEIVER_CAP_SIZE 0xf #define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) #define DP_LINK_CONFIGURATION_SIZE 9 struct intel_dp { struct intel_encoder base; uint32_t output_reg; uint32_t DP; uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; bool has_audio; enum hdmi_force_audio force_audio; uint32_t color_range; int dpms_mode; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; device_t dp_iic_bus; device_t adapter; bool is_pch_edp; uint8_t train_set[4]; int panel_power_up_delay; int panel_power_down_delay; int panel_power_cycle_delay; int backlight_on_delay; int backlight_off_delay; struct drm_display_mode *panel_fixed_mode; /* for eDP */ struct timeout_task panel_vdd_task; bool want_panel_vdd; }; /** * is_edp - is the given port attached to an eDP panel (either CPU or PCH) * @intel_dp: DP struct * * If a CPU or PCH DP output is attached to an eDP panel, this function * will return true, and false otherwise. */ static bool is_edp(struct intel_dp *intel_dp) { return intel_dp->base.type == INTEL_OUTPUT_EDP; } /** * is_pch_edp - is the port on the PCH and attached to an eDP panel? * @intel_dp: DP struct * * Returns true if the given DP struct corresponds to a PCH DP port attached * to an eDP panel, false otherwise. Helpful for determining whether we * may need FDI resources for a given DP output or not. */ static bool is_pch_edp(struct intel_dp *intel_dp) { return intel_dp->is_pch_edp; } /** * is_cpu_edp - is the port on the CPU and attached to an eDP panel? * @intel_dp: DP struct * * Returns true if the given DP struct corresponds to a CPU eDP port. */ static bool is_cpu_edp(struct intel_dp *intel_dp) { return is_edp(intel_dp) && !is_pch_edp(intel_dp); } static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) { return container_of(encoder, struct intel_dp, base.base); } static struct intel_dp *intel_attached_dp(struct drm_connector *connector) { return container_of(intel_attached_encoder(connector), struct intel_dp, base); } /** * intel_encoder_is_pch_edp - is the given encoder a PCH attached eDP? * @encoder: DRM encoder * * Return true if @encoder corresponds to a PCH attached eDP panel. Needed * by intel_display.c. */ bool intel_encoder_is_pch_edp(struct drm_encoder *encoder) { struct intel_dp *intel_dp; if (!encoder) return false; intel_dp = enc_to_intel_dp(encoder); return is_pch_edp(intel_dp); } static void intel_dp_start_link_train(struct intel_dp *intel_dp); static void intel_dp_complete_link_train(struct intel_dp *intel_dp); static void intel_dp_link_down(struct intel_dp *intel_dp); void intel_edp_link_config(struct intel_encoder *intel_encoder, int *lane_num, int *link_bw) { struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); *lane_num = intel_dp->lane_count; if (intel_dp->link_bw == DP_LINK_BW_1_62) *link_bw = 162000; else if (intel_dp->link_bw == DP_LINK_BW_2_7) *link_bw = 270000; } static int intel_dp_max_lane_count(struct intel_dp *intel_dp) { int max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; switch (max_lane_count) { case 1: case 2: case 4: break; default: max_lane_count = 4; } return max_lane_count; } static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; switch (max_link_bw) { case DP_LINK_BW_1_62: case DP_LINK_BW_2_7: break; default: max_link_bw = DP_LINK_BW_1_62; break; } return max_link_bw; } static int intel_dp_link_clock(uint8_t link_bw) { if (link_bw == DP_LINK_BW_2_7) return 270000; else return 162000; } /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. * * intel_dp_max_data_rate for one lane of 2.7GHz evaluates as: * * 270000 * 1 * 8 / 10 == 216000 * * The actual data capacity of that configuration is 2.16Gbit/s, so the * units are decakilobits. ->clock in a drm_display_mode is in kilohertz - * or equivalently, kilopixels per second - so for 1680x1050R it'd be * 119000. At 18bpp that's 2142000 kilobits per second. * * Thus the strange-looking division by 10 in intel_dp_link_required, to * get the result in decakilobits instead of kilobits. */ static int intel_dp_link_required(int pixel_clock, int bpp) { return (pixel_clock * bpp + 9) / 10; } static int intel_dp_max_data_rate(int max_link_clock, int max_lanes) { return (max_link_clock * max_lanes * 8) / 10; } static bool intel_dp_adjust_dithering(struct intel_dp *intel_dp, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); int max_lanes = intel_dp_max_lane_count(intel_dp); int max_rate, mode_rate; mode_rate = intel_dp_link_required(mode->clock, 24); max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); if (mode_rate > max_rate) { mode_rate = intel_dp_link_required(mode->clock, 18); if (mode_rate > max_rate) return false; if (adjusted_mode) adjusted_mode->private_flags |= INTEL_MODE_DP_FORCE_6BPC; return true; } return true; } static int intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_dp *intel_dp = intel_attached_dp(connector); if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL; } if (!intel_dp_adjust_dithering(intel_dp, mode, NULL)) return MODE_CLOCK_HIGH; if (mode->clock < 10000) return MODE_CLOCK_LOW; return MODE_OK; } static uint32_t pack_aux(uint8_t *src, int src_bytes) { int i; uint32_t v = 0; if (src_bytes > 4) src_bytes = 4; for (i = 0; i < src_bytes; i++) v |= ((uint32_t) src[i]) << ((3-i) * 8); return v; } static void unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) { int i; if (dst_bytes > 4) dst_bytes = 4; for (i = 0; i < dst_bytes; i++) dst[i] = src >> ((3-i) * 8); } /* hrawclock is 1/4 the FSB frequency */ static int intel_hrawclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t clkcfg; clkcfg = I915_READ(CLKCFG); switch (clkcfg & CLKCFG_FSB_MASK) { case CLKCFG_FSB_400: return 100; case CLKCFG_FSB_533: return 133; case CLKCFG_FSB_667: return 166; case CLKCFG_FSB_800: return 200; case CLKCFG_FSB_1067: return 266; case CLKCFG_FSB_1333: return 333; /* these two are just a guess; one of them might be right */ case CLKCFG_FSB_1600: case CLKCFG_FSB_1600_ALT: return 400; default: return 133; } } static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0; } static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0; } static void intel_dp_check_edp(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; if (!is_edp(intel_dp)) return; if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { printf("eDP powered off while attempting aux channel communication.\n"); DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); } } static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, uint8_t *recv, int recv_size) { uint32_t output_reg = intel_dp->output_reg; struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; int i; int recv_bytes; uint32_t status; uint32_t aux_clock_divider; int try, precharge = 5; intel_dp_check_edp(intel_dp); /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that * * Note that PCH attached eDP panels should use a 125MHz input * clock divider. */ if (is_cpu_edp(intel_dp)) { if (IS_GEN6(dev) || IS_GEN7(dev)) aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */ else aux_clock_divider = 225; /* eDP input clock at 450Mhz */ } else if (HAS_PCH_SPLIT(dev)) aux_clock_divider = 63; /* IRL input clock fixed at 125Mhz */ else aux_clock_divider = intel_hrawclk(dev) / 2; /* Try to wait for any previous AUX channel activity */ for (try = 0; try < 3; try++) { status = I915_READ(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break; drm_msleep(1, "915ach"); } if (try == 3) { printf("dp_aux_ch not started status 0x%08x\n", I915_READ(ch_ctl)); return -EBUSY; } /* Must try at least 3 times according to DP spec */ for (try = 0; try < 5; try++) { /* Load the send data into the aux channel data registers */ for (i = 0; i < send_bytes; i += 4) I915_WRITE(ch_data + i, pack_aux(send + i, send_bytes - i)); /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); for (;;) { status = I915_READ(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break; DELAY(100); } /* Clear done status and any errors */ I915_WRITE(ch_ctl, status | DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR)) continue; if (status & DP_AUX_CH_CTL_DONE) break; } if ((status & DP_AUX_CH_CTL_DONE) == 0) { DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); return -EBUSY; } /* Check for timeout or receive error. * Timeouts occur when the sink is not connected */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); return -EIO; } /* Timeouts occur when the device isn't connected, so they're * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); return -ETIMEDOUT; } /* Unload any bytes sent back from the other side */ recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); if (recv_bytes > recv_size) recv_bytes = recv_size; for (i = 0; i < recv_bytes; i += 4) unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); return recv_bytes; } /* Write data to the aux channel in native mode */ static int intel_dp_aux_native_write(struct intel_dp *intel_dp, uint16_t address, uint8_t *send, int send_bytes) { int ret; uint8_t msg[20]; int msg_bytes; uint8_t ack; intel_dp_check_edp(intel_dp); if (send_bytes > 16) return -1; msg[0] = AUX_NATIVE_WRITE << 4; msg[1] = address >> 8; msg[2] = address & 0xff; msg[3] = send_bytes - 1; memcpy(&msg[4], send, send_bytes); msg_bytes = send_bytes + 4; for (;;) { ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1); if (ret < 0) return ret; if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) break; else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) DELAY(100); else return -EIO; } return send_bytes; } /* Write a single byte to the aux channel in native mode */ static int intel_dp_aux_native_write_1(struct intel_dp *intel_dp, uint16_t address, uint8_t byte) { return intel_dp_aux_native_write(intel_dp, address, &byte, 1); } /* read bytes from a native aux channel */ static int intel_dp_aux_native_read(struct intel_dp *intel_dp, uint16_t address, uint8_t *recv, int recv_bytes) { uint8_t msg[4]; int msg_bytes; uint8_t reply[20]; int reply_bytes; uint8_t ack; int ret; intel_dp_check_edp(intel_dp); msg[0] = AUX_NATIVE_READ << 4; msg[1] = address >> 8; msg[2] = address & 0xff; msg[3] = recv_bytes - 1; msg_bytes = 4; reply_bytes = recv_bytes + 1; for (;;) { ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, reply, reply_bytes); if (ret == 0) return -EPROTO; if (ret < 0) return ret; ack = reply[0]; if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) { memcpy(recv, reply + 1, ret - 1); return ret - 1; } else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) DELAY(100); else return -EIO; } } static int intel_dp_i2c_aux_ch(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte) { struct iic_dp_aux_data *data; struct intel_dp *intel_dp; uint16_t address; uint8_t msg[5]; uint8_t reply[2]; unsigned retry; int msg_bytes; int reply_bytes; int ret; data = device_get_softc(idev); intel_dp = data->priv; address = data->address; intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) msg[0] = AUX_I2C_READ << 4; else msg[0] = AUX_I2C_WRITE << 4; if (!(mode & MODE_I2C_STOP)) msg[0] |= AUX_I2C_MOT << 4; msg[1] = address >> 8; msg[2] = address; switch (mode) { case MODE_I2C_WRITE: msg[3] = 0; msg[4] = write_byte; msg_bytes = 5; reply_bytes = 1; break; case MODE_I2C_READ: msg[3] = 0; msg_bytes = 4; reply_bytes = 2; break; default: msg_bytes = 3; reply_bytes = 1; break; } for (retry = 0; retry < 5; retry++) { ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, reply, reply_bytes); if (ret < 0) { DRM_DEBUG_KMS("aux_ch failed %d\n", ret); - return (-ret); + return (ret); } switch (reply[0] & AUX_NATIVE_REPLY_MASK) { case AUX_NATIVE_REPLY_ACK: /* I2C-over-AUX Reply field is only valid * when paired with AUX ACK. */ break; case AUX_NATIVE_REPLY_NACK: DRM_DEBUG_KMS("aux_ch native nack\n"); - return (EREMOTEIO); + return (-EREMOTEIO); case AUX_NATIVE_REPLY_DEFER: DELAY(100); continue; default: DRM_ERROR("aux_ch invalid native reply 0x%02x\n", reply[0]); - return (EREMOTEIO); + return (-EREMOTEIO); } switch (reply[0] & AUX_I2C_REPLY_MASK) { case AUX_I2C_REPLY_ACK: if (mode == MODE_I2C_READ) { *read_byte = reply[1]; } return (0/*reply_bytes - 1*/); case AUX_I2C_REPLY_NACK: DRM_DEBUG_KMS("aux_i2c nack\n"); - return (EREMOTEIO); + return (-EREMOTEIO); case AUX_I2C_REPLY_DEFER: DRM_DEBUG_KMS("aux_i2c defer\n"); DELAY(100); break; default: DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); - return (EREMOTEIO); + return (-EREMOTEIO); } } DRM_ERROR("too many retries, giving up\n"); - return (EREMOTEIO); + return (-EREMOTEIO); } static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); static int intel_dp_i2c_init(struct intel_dp *intel_dp, struct intel_connector *intel_connector, const char *name) { int ret; DRM_DEBUG_KMS("i2c_init %s\n", name); ironlake_edp_panel_vdd_on(intel_dp); ret = iic_dp_aux_add_bus(intel_connector->base.dev->device, name, intel_dp_i2c_aux_ch, intel_dp, &intel_dp->dp_iic_bus, &intel_dp->adapter); ironlake_edp_panel_vdd_off(intel_dp, false); return (ret); } static bool intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; int bpp, mode_rate; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN, mode, adjusted_mode); /* * the mode->clock is used to calculate the Data&Link M/N * of the pipe. For the eDP the fixed clock should be used. */ mode->clock = intel_dp->panel_fixed_mode->clock; } DRM_DEBUG_KMS("DP link computation with max lane count %i " "max bw %02x pixel clock %iKHz\n", max_lane_count, bws[max_clock], mode->clock); if (!intel_dp_adjust_dithering(intel_dp, adjusted_mode, adjusted_mode)) return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; mode_rate = intel_dp_link_required(mode->clock, bpp); for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); DRM_DEBUG_KMS("DP link bw %02x lane " "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, adjusted_mode->clock, bpp); DRM_DEBUG_KMS("DP link bw required %i available %i\n", mode_rate, link_avail); return true; } } } return false; } struct intel_dp_m_n { uint32_t tu; uint32_t gmch_m; uint32_t gmch_n; uint32_t link_m; uint32_t link_n; }; static void intel_reduce_ratio(uint32_t *num, uint32_t *den) { while (*num > 0xffffff || *den > 0xffffff) { *num >>= 1; *den >>= 1; } } static void intel_dp_compute_m_n(int bpp, int nlanes, int pixel_clock, int link_clock, struct intel_dp_m_n *m_n) { m_n->tu = 64; m_n->gmch_m = (pixel_clock * bpp) >> 3; m_n->gmch_n = link_clock * nlanes; intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; intel_reduce_ratio(&m_n->link_m, &m_n->link_n); } void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_encoder *encoder; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int lane_count = 4; struct intel_dp_m_n m_n; int pipe = intel_crtc->pipe; /* * Find the lane count in the intel_encoder private */ list_for_each_entry(encoder, &mode_config->encoder_list, head) { struct intel_dp *intel_dp; if (encoder->crtc != crtc) continue; intel_dp = enc_to_intel_dp(encoder); if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT || intel_dp->base.type == INTEL_OUTPUT_EDP) { lane_count = intel_dp->lane_count; break; } } /* * Compute the GMCH and Link ratios. The '3' here is * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ intel_dp_compute_m_n(intel_crtc->bpp, lane_count, mode->clock, adjusted_mode->clock, &m_n); if (HAS_PCH_SPLIT(dev)) { I915_WRITE(TRANSDATA_M1(pipe), ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | m_n.gmch_m); I915_WRITE(TRANSDATA_N1(pipe), m_n.gmch_n); I915_WRITE(TRANSDPLINK_M1(pipe), m_n.link_m); I915_WRITE(TRANSDPLINK_N1(pipe), m_n.link_n); } else { I915_WRITE(PIPE_GMCH_DATA_M(pipe), ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | m_n.gmch_m); I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); I915_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); I915_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); } } static void ironlake_edp_pll_on(struct drm_encoder *encoder); static void ironlake_edp_pll_off(struct drm_encoder *encoder); static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_crtc *crtc = intel_dp->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* Turn on the eDP PLL if needed */ if (is_edp(intel_dp)) { if (!is_pch_edp(intel_dp)) ironlake_edp_pll_on(encoder); else ironlake_edp_pll_off(encoder); } /* * There are four kinds of DP registers: * * IBX PCH * SNB CPU * IVB CPU * CPT PCH * * IBX PCH and CPU are the same for almost everything, * except that the CPU DP PLL is configured in this * register * * CPT PCH is quite different, having many bits moved * to the TRANS_DP_CTL register instead. That * configuration happens (oddly) in ironlake_pch_enable */ /* Preserve the BIOS-computed detected bit. This is * supposed to be read-only. */ intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED; intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; /* Handle DP bits in common between all three register formats */ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; switch (intel_dp->lane_count) { case 1: intel_dp->DP |= DP_PORT_WIDTH_1; break; case 2: intel_dp->DP |= DP_PORT_WIDTH_2; break; case 4: intel_dp->DP |= DP_PORT_WIDTH_4; break; } if (intel_dp->has_audio) { DRM_DEBUG_KMS("Enabling DP audio on pipe %c\n", pipe_name(intel_crtc->pipe)); intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; intel_write_eld(encoder, adjusted_mode); } memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); intel_dp->link_configuration[0] = intel_dp->link_bw; intel_dp->link_configuration[1] = intel_dp->lane_count; /* * Check for DPCD version > 1.1 and enhanced framing support */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } /* Split out the IBX/CPU vs CPT settings */ if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) { if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) intel_dp->DP |= DP_SYNC_VS_HIGH; intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) intel_dp->DP |= DP_ENHANCED_FRAMING; intel_dp->DP |= intel_crtc->pipe << 29; /* don't miss out required setting for eDP */ intel_dp->DP |= DP_PLL_ENABLE; if (adjusted_mode->clock < 200000) intel_dp->DP |= DP_PLL_FREQ_160MHZ; else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { intel_dp->DP |= intel_dp->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) intel_dp->DP |= DP_SYNC_VS_HIGH; intel_dp->DP |= DP_LINK_TRAIN_OFF; if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN) intel_dp->DP |= DP_ENHANCED_FRAMING; if (intel_crtc->pipe == 1) intel_dp->DP |= DP_PIPEB_SELECT; if (is_cpu_edp(intel_dp)) { /* don't miss out required setting for eDP */ intel_dp->DP |= DP_PLL_ENABLE; if (adjusted_mode->clock < 200000) intel_dp->DP |= DP_PLL_FREQ_160MHZ; else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } } else { intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; } } #define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) #define IDLE_ON_VALUE (PP_ON | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE) #define IDLE_OFF_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) #define IDLE_OFF_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) #define IDLE_CYCLE_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK) #define IDLE_CYCLE_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE) static void ironlake_wait_panel_status(struct intel_dp *intel_dp, u32 mask, u32 value) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; DRM_DEBUG_KMS("mask %08x value %08x status %08x control %08x\n", mask, value, I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); if (_intel_wait_for(dev, (I915_READ(PCH_PP_STATUS) & mask) == value, 5000, 10, "915iwp")) { DRM_ERROR("Panel status timeout: status %08x control %08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); } } static void ironlake_wait_panel_on(struct intel_dp *intel_dp) { DRM_DEBUG_KMS("Wait for panel power on\n"); ironlake_wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE); } static void ironlake_wait_panel_off(struct intel_dp *intel_dp) { DRM_DEBUG_KMS("Wait for panel power off time\n"); ironlake_wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE); } static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp) { DRM_DEBUG_KMS("Wait for panel power cycle\n"); ironlake_wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE); } /* Read the current pp_control value, unlocking the register if it * is locked */ static u32 ironlake_get_pp_control(struct drm_i915_private *dev_priv) { u32 control = I915_READ(PCH_PP_CONTROL); control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; return control; } static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("Turn eDP VDD on\n"); if (intel_dp->want_panel_vdd) printf("eDP VDD already requested on\n"); intel_dp->want_panel_vdd = true; if (ironlake_edp_have_panel_vdd(intel_dp)) { DRM_DEBUG_KMS("eDP VDD already on\n"); return; } if (!ironlake_edp_have_panel_power(intel_dp)) ironlake_wait_panel_power_cycle(intel_dp); pp = ironlake_get_pp_control(dev_priv); pp |= EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); /* * If the panel wasn't on, delay before accessing aux channel */ if (!ironlake_edp_have_panel_power(intel_dp)) { DRM_DEBUG_KMS("eDP was not running\n"); drm_msleep(intel_dp->panel_power_up_delay, "915edpon"); } } static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); /* Make sure sequencer is idle before allowing subsequent activity */ DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); drm_msleep(intel_dp->panel_power_down_delay, "915vddo"); } } static void ironlake_panel_vdd_work(void *arg, int pending __unused) { struct intel_dp *intel_dp = arg; struct drm_device *dev = intel_dp->base.base.dev; sx_xlock(&dev->mode_config.mutex); ironlake_panel_vdd_off_sync(intel_dp); sx_xunlock(&dev->mode_config.mutex); } static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) { if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd); if (!intel_dp->want_panel_vdd) printf("eDP VDD not forced on\n"); intel_dp->want_panel_vdd = false; if (sync) { ironlake_panel_vdd_off_sync(intel_dp); } else { /* * Queue the timer to fire a long * time from now (relative to the power down delay) * to keep the panel power up across a sequence of operations */ struct drm_i915_private *dev_priv = intel_dp->base.base.dev->dev_private; taskqueue_enqueue_timeout(dev_priv->tq, &intel_dp->panel_vdd_task, msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5)); } } static void ironlake_edp_panel_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("Turn eDP power on\n"); if (ironlake_edp_have_panel_power(intel_dp)) { DRM_DEBUG_KMS("eDP power already on\n"); return; } ironlake_wait_panel_power_cycle(intel_dp); pp = ironlake_get_pp_control(dev_priv); if (IS_GEN5(dev)) { /* ILK workaround: disable reset around power sequence */ pp &= ~PANEL_POWER_RESET; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); } pp |= POWER_TARGET_ON; if (!IS_GEN5(dev)) pp |= PANEL_POWER_RESET; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); ironlake_wait_panel_on(intel_dp); if (IS_GEN5(dev)) { pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); } } static void ironlake_edp_panel_off(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("Turn eDP power off\n"); if (intel_dp->want_panel_vdd) printf("Cannot turn power off while VDD is on\n"); ironlake_panel_vdd_off_sync(intel_dp); /* finish any pending work */ pp = ironlake_get_pp_control(dev_priv); pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE); I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); ironlake_wait_panel_off(intel_dp); } static void ironlake_edp_backlight_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("\n"); /* * If we enable the backlight right away following a panel power * on, we may see slight flicker as the panel syncs with the eDP * link. So delay a bit to make sure the image is solid before * allowing it to appear. */ drm_msleep(intel_dp->backlight_on_delay, "915ebo"); pp = ironlake_get_pp_control(dev_priv); pp |= EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); } static void ironlake_edp_backlight_off(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("\n"); pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); drm_msleep(intel_dp->backlight_off_delay, "915bo1"); } static void ironlake_edp_pll_on(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 dpa_ctl; DRM_DEBUG_KMS("\n"); dpa_ctl = I915_READ(DP_A); dpa_ctl |= DP_PLL_ENABLE; I915_WRITE(DP_A, dpa_ctl); POSTING_READ(DP_A); DELAY(200); } static void ironlake_edp_pll_off(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 dpa_ctl; dpa_ctl = I915_READ(DP_A); dpa_ctl &= ~DP_PLL_ENABLE; I915_WRITE(DP_A, dpa_ctl); POSTING_READ(DP_A); DELAY(200); } /* If the sink supports it, try to set the power state appropriately */ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) { int ret, i; /* Should have a valid DPCD by this point */ if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) return; if (mode != DRM_MODE_DPMS_ON) { ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER, DP_SET_POWER_D3); if (ret != 1) DRM_DEBUG("failed to write sink power state\n"); } else { /* * When turning on, we need to retry for 1ms to give the sink * time to wake up. */ for (i = 0; i < 3; i++) { ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER, DP_SET_POWER_D0); if (ret == 1) break; drm_msleep(1, "915dps"); } } } static void intel_dp_prepare(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_off(intel_dp); ironlake_edp_panel_off(intel_dp); /* Wake up the sink first */ ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_link_down(intel_dp); ironlake_edp_panel_vdd_off(intel_dp, false); /* Make sure the panel is off before trying to * change the mode */ } static void intel_dp_commit(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); intel_dp->dpms_mode = DRM_MODE_DPMS_ON; if (HAS_PCH_CPT(dev)) intel_cpt_verify_modeset(dev, intel_crtc->pipe); } static void intel_dp_dpms(struct drm_encoder *encoder, int mode) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dp_reg = I915_READ(intel_dp->output_reg); if (mode != DRM_MODE_DPMS_ON) { ironlake_edp_backlight_off(intel_dp); ironlake_edp_panel_off(intel_dp); ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); intel_dp_link_down(intel_dp); ironlake_edp_panel_vdd_off(intel_dp, false); if (is_cpu_edp(intel_dp)) ironlake_edp_pll_off(encoder); } else { if (is_cpu_edp(intel_dp)) ironlake_edp_pll_on(encoder); ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); } else ironlake_edp_panel_vdd_off(intel_dp, false); ironlake_edp_backlight_on(intel_dp); } intel_dp->dpms_mode = mode; } /* * Native read with retry for link status and receiver capability reads for * cases where the sink may still be asleep. */ static bool intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address, uint8_t *recv, int recv_bytes) { int ret, i; /* * Sinks are *supposed* to come up within 1ms from an off state, * but we're also supposed to retry 3 times per the spec. */ for (i = 0; i < 3; i++) { ret = intel_dp_aux_native_read(intel_dp, address, recv, recv_bytes); if (ret == recv_bytes) return true; drm_msleep(1, "915dpl"); } return false; } /* * Fetch AUX CH registers 0x202 - 0x207 which contain * link status information */ static bool intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { return intel_dp_aux_native_read_retry(intel_dp, DP_LANE0_1_STATUS, link_status, DP_LINK_STATUS_SIZE); } static uint8_t intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r) { return link_status[r - DP_LANE0_1_STATUS]; } static uint8_t intel_get_adjust_request_voltage(uint8_t adjust_request[2], int lane) { int s = ((lane & 1) ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); uint8_t l = adjust_request[lane>>1]; return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; } static uint8_t intel_get_adjust_request_pre_emphasis(uint8_t adjust_request[2], int lane) { int s = ((lane & 1) ? DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); uint8_t l = adjust_request[lane>>1]; return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; } #if 0 static char *voltage_names[] = { "0.4V", "0.6V", "0.8V", "1.2V" }; static char *pre_emph_names[] = { "0dB", "3.5dB", "6dB", "9.5dB" }; static char *link_train_names[] = { "pattern 1", "pattern 2", "idle", "off" }; #endif /* * These are source-specific values; current Intel hardware supports * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB */ static uint8_t intel_dp_voltage_max(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) return DP_TRAIN_VOLTAGE_SWING_800; else if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) return DP_TRAIN_VOLTAGE_SWING_1200; else return DP_TRAIN_VOLTAGE_SWING_800; } static uint8_t intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) { struct drm_device *dev = intel_dp->base.base.dev; if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: return DP_TRAIN_PRE_EMPHASIS_6; case DP_TRAIN_VOLTAGE_SWING_600: case DP_TRAIN_VOLTAGE_SWING_800: return DP_TRAIN_PRE_EMPHASIS_3_5; default: return DP_TRAIN_PRE_EMPHASIS_0; } } else { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: return DP_TRAIN_PRE_EMPHASIS_6; case DP_TRAIN_VOLTAGE_SWING_600: return DP_TRAIN_PRE_EMPHASIS_6; case DP_TRAIN_VOLTAGE_SWING_800: return DP_TRAIN_PRE_EMPHASIS_3_5; case DP_TRAIN_VOLTAGE_SWING_1200: default: return DP_TRAIN_PRE_EMPHASIS_0; } } } static void intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { uint8_t v = 0; uint8_t p = 0; int lane; uint8_t *adjust_request = link_status + (DP_ADJUST_REQUEST_LANE0_1 - DP_LANE0_1_STATUS); uint8_t voltage_max; uint8_t preemph_max; for (lane = 0; lane < intel_dp->lane_count; lane++) { uint8_t this_v = intel_get_adjust_request_voltage(adjust_request, lane); uint8_t this_p = intel_get_adjust_request_pre_emphasis(adjust_request, lane); if (this_v > v) v = this_v; if (this_p > p) p = this_p; } voltage_max = intel_dp_voltage_max(intel_dp); if (v >= voltage_max) v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; preemph_max = intel_dp_pre_emphasis_max(intel_dp, v); if (p >= preemph_max) p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; for (lane = 0; lane < 4; lane++) intel_dp->train_set[lane] = v | p; } static uint32_t intel_dp_signal_levels(uint8_t train_set) { uint32_t signal_levels = 0; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: default: signal_levels |= DP_VOLTAGE_0_4; break; case DP_TRAIN_VOLTAGE_SWING_600: signal_levels |= DP_VOLTAGE_0_6; break; case DP_TRAIN_VOLTAGE_SWING_800: signal_levels |= DP_VOLTAGE_0_8; break; case DP_TRAIN_VOLTAGE_SWING_1200: signal_levels |= DP_VOLTAGE_1_2; break; } switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { case DP_TRAIN_PRE_EMPHASIS_0: default: signal_levels |= DP_PRE_EMPHASIS_0; break; case DP_TRAIN_PRE_EMPHASIS_3_5: signal_levels |= DP_PRE_EMPHASIS_3_5; break; case DP_TRAIN_PRE_EMPHASIS_6: signal_levels |= DP_PRE_EMPHASIS_6; break; case DP_TRAIN_PRE_EMPHASIS_9_5: signal_levels |= DP_PRE_EMPHASIS_9_5; break; } return signal_levels; } /* Gen6's DP voltage swing and pre-emphasis control */ static uint32_t intel_gen6_edp_signal_levels(uint8_t train_set) { int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B; case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0: return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B; default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" "0x%x\n", signal_levels); return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; } } /* Gen7's DP voltage swing and pre-emphasis control */ static uint32_t intel_gen7_edp_signal_levels(uint8_t train_set) { int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: return EDP_LINK_TRAIN_400MV_0DB_IVB; case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: return EDP_LINK_TRAIN_400MV_3_5DB_IVB; case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: return EDP_LINK_TRAIN_400MV_6DB_IVB; case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: return EDP_LINK_TRAIN_600MV_0DB_IVB; case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: return EDP_LINK_TRAIN_600MV_3_5DB_IVB; case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: return EDP_LINK_TRAIN_800MV_0DB_IVB; case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: return EDP_LINK_TRAIN_800MV_3_5DB_IVB; default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" "0x%x\n", signal_levels); return EDP_LINK_TRAIN_500MV_0DB_IVB; } } static uint8_t intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { int s = (lane & 1) * 4; uint8_t l = link_status[lane>>1]; return (l >> s) & 0xf; } /* Check for clock recovery is done on all channels */ static bool intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) { int lane; uint8_t lane_status; for (lane = 0; lane < lane_count; lane++) { lane_status = intel_get_lane_status(link_status, lane); if ((lane_status & DP_LANE_CR_DONE) == 0) return false; } return true; } /* Check to see if channel eq is done on all channels */ #define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ DP_LANE_CHANNEL_EQ_DONE|\ DP_LANE_SYMBOL_LOCKED) static bool intel_channel_eq_ok(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { uint8_t lane_align; uint8_t lane_status; int lane; lane_align = intel_dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; for (lane = 0; lane < intel_dp->lane_count; lane++) { lane_status = intel_get_lane_status(link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) return false; } return true; } static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, uint8_t dp_train_pat) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; int ret; I915_WRITE(intel_dp->output_reg, dp_reg_value); POSTING_READ(intel_dp->output_reg); intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET, dp_train_pat); ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET, intel_dp->train_set, intel_dp->lane_count); if (ret != intel_dp->lane_count) return false; return true; } /* Enable corresponding port and start training pattern 1 */ static void intel_dp_start_link_train(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); int i; uint8_t voltage; bool clock_recovery = false; int voltage_tries, loop_tries; u32 reg; uint32_t DP = intel_dp->DP; /* Enable output, wait for it to become active */ I915_WRITE(intel_dp->output_reg, intel_dp->DP); POSTING_READ(intel_dp->output_reg); intel_wait_for_vblank(dev, intel_crtc->pipe); /* Write the link configuration data */ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET, intel_dp->link_configuration, DP_LINK_CONFIGURATION_SIZE); DP |= DP_PORT_EN; if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) DP &= ~DP_LINK_TRAIN_MASK_CPT; else DP &= ~DP_LINK_TRAIN_MASK; memset(intel_dp->train_set, 0, 4); voltage = 0xff; voltage_tries = 0; loop_tries = 0; clock_recovery = false; for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint8_t link_status[DP_LINK_STATUS_SIZE]; uint32_t signal_levels; if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; } else { signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", signal_levels); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_1_CPT; else reg = DP | DP_LINK_TRAIN_PAT_1; if (!intel_dp_set_link_train(intel_dp, reg, DP_TRAINING_PATTERN_1)) break; /* Set training pattern 1 */ DELAY(100); if (!intel_dp_get_link_status(intel_dp, link_status)) { DRM_ERROR("failed to get link status\n"); break; } if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("clock recovery OK\n"); clock_recovery = true; break; } /* Check to see if we've tried the max voltage */ for (i = 0; i < intel_dp->lane_count; i++) if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) break; if (i == intel_dp->lane_count) { ++loop_tries; if (loop_tries == 5) { DRM_DEBUG_KMS("too many full retries, give up\n"); break; } memset(intel_dp->train_set, 0, 4); voltage_tries = 0; continue; } /* Check to see if we've tried the same voltage 5 times */ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { ++voltage_tries; if (voltage_tries == 5) { DRM_DEBUG_KMS("too many voltage retries, give up\n"); break; } } else voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Compute new intel_dp->train_set as requested by target */ intel_get_adjust_train(intel_dp, link_status); } intel_dp->DP = DP; } static void intel_dp_complete_link_train(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; bool channel_eq = false; int tries, cr_tries; u32 reg; uint32_t DP = intel_dp->DP; /* channel equalization */ tries = 0; cr_tries = 0; channel_eq = false; for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint32_t signal_levels; uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); intel_dp_link_down(intel_dp); break; } if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; } else { signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_PAT_2_CPT; else reg = DP | DP_LINK_TRAIN_PAT_2; /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, reg, DP_TRAINING_PATTERN_2)) break; DELAY(400); if (!intel_dp_get_link_status(intel_dp, link_status)) break; /* Make sure clock is still ok */ if (!intel_clock_recovery_ok(link_status, intel_dp->lane_count)) { intel_dp_start_link_train(intel_dp); cr_tries++; continue; } if (intel_channel_eq_ok(intel_dp, link_status)) { channel_eq = true; break; } /* Try 5 times, then try clock recovery if that fails */ if (tries > 5) { intel_dp_link_down(intel_dp); intel_dp_start_link_train(intel_dp); tries = 0; cr_tries++; continue; } /* Compute new intel_dp->train_set as requested by target */ intel_get_adjust_train(intel_dp, link_status); ++tries; } if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) reg = DP | DP_LINK_TRAIN_OFF_CPT; else reg = DP | DP_LINK_TRAIN_OFF; I915_WRITE(intel_dp->output_reg, reg); POSTING_READ(intel_dp->output_reg); intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); } static void intel_dp_link_down(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t DP = intel_dp->DP; if ((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) return; DRM_DEBUG_KMS("\n"); if (is_edp(intel_dp)) { DP &= ~DP_PLL_ENABLE; I915_WRITE(intel_dp->output_reg, DP); POSTING_READ(intel_dp->output_reg); DELAY(100); } if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) { DP &= ~DP_LINK_TRAIN_MASK_CPT; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); } else { DP &= ~DP_LINK_TRAIN_MASK; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); } POSTING_READ(intel_dp->output_reg); drm_msleep(17, "915dlo"); if (is_edp(intel_dp)) { if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) DP |= DP_LINK_TRAIN_OFF_CPT; else DP |= DP_LINK_TRAIN_OFF; } if (!HAS_PCH_CPT(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { struct drm_crtc *crtc = intel_dp->base.base.crtc; /* Hardware workaround: leaving our transcoder select * set to transcoder B while it's off will prevent the * corresponding HDMI output on transcoder A. * * Combine this with another hardware workaround: * transcoder select bit can only be cleared while the * port is enabled. */ DP &= ~DP_PIPEB_SELECT; I915_WRITE(intel_dp->output_reg, DP); /* Changes to enable or select take place the vblank * after being written. */ if (crtc == NULL) { /* We can arrive here never having been attached * to a CRTC, for instance, due to inheriting * random state from the BIOS. * * If the pipe is not running, play safe and * wait for the clocks to stabilise before * continuing. */ POSTING_READ(intel_dp->output_reg); drm_msleep(50, "915dla"); } else intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); } DP &= ~DP_AUDIO_OUTPUT_ENABLE; I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); POSTING_READ(intel_dp->output_reg); drm_msleep(intel_dp->panel_power_down_delay, "915ldo"); } static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) && (intel_dp->dpcd[DP_DPCD_REV] != 0)) { return true; } return false; } static void intel_dp_probe_oui(struct intel_dp *intel_dp) { u8 buf[3]; if (!(intel_dp->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT)) return; if (intel_dp_aux_native_read_retry(intel_dp, DP_SINK_OUI, buf, 3)) DRM_DEBUG_KMS("Sink OUI: %02x%02x%02x\n", buf[0], buf[1], buf[2]); if (intel_dp_aux_native_read_retry(intel_dp, DP_BRANCH_OUI, buf, 3)) DRM_DEBUG_KMS("Branch OUI: %02x%02x%02x\n", buf[0], buf[1], buf[2]); } static bool intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) { int ret; ret = intel_dp_aux_native_read_retry(intel_dp, DP_DEVICE_SERVICE_IRQ_VECTOR, sink_irq_vector, 1); if (!ret) return false; return true; } static void intel_dp_handle_test_request(struct intel_dp *intel_dp) { /* NAK by default */ intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_ACK); } /* * According to DP spec * 5.1.2: * 1. Read DPCD * 2. Configure link according to Receiver Capabilities * 3. Use Link Training from 2.5.3.3 and 3.5.1.3 * 4. Check link status on receipt of hot-plug interrupt */ static void intel_dp_check_link_status(struct intel_dp *intel_dp) { u8 sink_irq_vector; u8 link_status[DP_LINK_STATUS_SIZE]; if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) return; if (!intel_dp->base.base.crtc) return; /* Try to read receiver status if the link appears to be up */ if (!intel_dp_get_link_status(intel_dp, link_status)) { intel_dp_link_down(intel_dp); return; } /* Now read the DPCD to see if it's actually running */ if (!intel_dp_get_dpcd(intel_dp)) { intel_dp_link_down(intel_dp); return; } /* Try to read the source of the interrupt */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { /* Clear interrupt source */ intel_dp_aux_native_write_1(intel_dp, DP_DEVICE_SERVICE_IRQ_VECTOR, sink_irq_vector); if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) intel_dp_handle_test_request(intel_dp); if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ)) DRM_DEBUG_KMS("CP or sink specific irq unhandled\n"); } if (!intel_channel_eq_ok(intel_dp, link_status)) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", drm_get_encoder_name(&intel_dp->base.base)); intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); } } static enum drm_connector_status intel_dp_detect_dpcd(struct intel_dp *intel_dp) { if (intel_dp_get_dpcd(intel_dp)) return connector_status_connected; return connector_status_disconnected; } static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { enum drm_connector_status status; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { status = intel_panel_detect(intel_dp->base.base.dev); if (status == connector_status_unknown) status = connector_status_connected; return status; } return intel_dp_detect_dpcd(intel_dp); } static enum drm_connector_status g4x_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t temp, bit; switch (intel_dp->output_reg) { case DP_B: bit = DPB_HOTPLUG_INT_STATUS; break; case DP_C: bit = DPC_HOTPLUG_INT_STATUS; break; case DP_D: bit = DPD_HOTPLUG_INT_STATUS; break; default: return connector_status_unknown; } temp = I915_READ(PORT_HOTPLUG_STAT); if ((temp & bit) == 0) return connector_status_disconnected; return intel_dp_detect_dpcd(intel_dp); } static struct edid * intel_dp_get_edid(struct drm_connector *connector, device_t adapter) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct edid *edid; ironlake_edp_panel_vdd_on(intel_dp); edid = drm_get_edid(connector, adapter); ironlake_edp_panel_vdd_off(intel_dp, false); return edid; } static int intel_dp_get_edid_modes(struct drm_connector *connector, device_t adapter) { struct intel_dp *intel_dp = intel_attached_dp(connector); int ret; ironlake_edp_panel_vdd_on(intel_dp); ret = intel_ddc_get_modes(connector, adapter); ironlake_edp_panel_vdd_off(intel_dp, false); return ret; } /** * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. * * \return true if DP port is connected. * \return false if DP port is disconnected. */ static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = intel_dp->base.base.dev; enum drm_connector_status status; struct edid *edid = NULL; intel_dp->has_audio = false; if (HAS_PCH_SPLIT(dev)) status = ironlake_dp_detect(intel_dp); else status = g4x_dp_detect(intel_dp); if (status != connector_status_connected) return status; intel_dp_probe_oui(intel_dp); if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); } else { edid = intel_dp_get_edid(connector, intel_dp->adapter); if (edid) { intel_dp->has_audio = drm_detect_monitor_audio(edid); connector->display_info.raw_edid = NULL; free(edid, DRM_MEM_KMS); } } return connector_status_connected; } static int intel_dp_get_modes(struct drm_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; int ret; /* We should parse the EDID data and find out if it has an audio sink */ ret = intel_dp_get_edid_modes(connector, intel_dp->adapter); if (ret) { if (is_edp(intel_dp) && !intel_dp->panel_fixed_mode) { struct drm_display_mode *newmode; list_for_each_entry(newmode, &connector->probed_modes, head) { if ((newmode->type & DRM_MODE_TYPE_PREFERRED)) { intel_dp->panel_fixed_mode = drm_mode_duplicate(dev, newmode); break; } } } return ret; } /* if eDP has no EDID, try to use fixed panel mode from VBT */ if (is_edp(intel_dp)) { /* initialize panel mode from VBT if available for eDP */ if (intel_dp->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) { intel_dp->panel_fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); if (intel_dp->panel_fixed_mode) { intel_dp->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; } } if (intel_dp->panel_fixed_mode) { struct drm_display_mode *mode; mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); drm_mode_probed_add(connector, mode); return 1; } } return 0; } static bool intel_dp_detect_audio(struct drm_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct edid *edid; bool has_audio = false; edid = intel_dp_get_edid(connector, intel_dp->adapter); if (edid) { has_audio = drm_detect_monitor_audio(edid); connector->display_info.raw_edid = NULL; free(edid, DRM_MEM_KMS); } return has_audio; } static int intel_dp_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { struct drm_i915_private *dev_priv = connector->dev->dev_private; struct intel_dp *intel_dp = intel_attached_dp(connector); int ret; ret = drm_connector_property_set_value(connector, property, val); if (ret) return ret; if (property == dev_priv->force_audio_property) { int i = val; bool has_audio; if (i == intel_dp->force_audio) return 0; intel_dp->force_audio = i; if (i == HDMI_AUDIO_AUTO) has_audio = intel_dp_detect_audio(connector); else has_audio = (i == HDMI_AUDIO_ON); if (has_audio == intel_dp->has_audio) return 0; intel_dp->has_audio = has_audio; goto done; } if (property == dev_priv->broadcast_rgb_property) { if (val == !!intel_dp->color_range) return 0; intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; goto done; } return -EINVAL; done: if (intel_dp->base.base.crtc) { struct drm_crtc *crtc = intel_dp->base.base.crtc; drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); } return 0; } static void intel_dp_destroy(struct drm_connector *connector) { struct drm_device *dev = connector->dev; if (intel_dpd_is_edp(dev)) intel_panel_destroy_backlight(dev); #if 0 drm_sysfs_connector_remove(connector); #endif drm_connector_cleanup(connector); free(connector, DRM_MEM_KMS); } static void intel_dp_encoder_destroy(struct drm_encoder *encoder) { struct drm_device *dev; struct intel_dp *intel_dp; intel_dp = enc_to_intel_dp(encoder); dev = encoder->dev; if (intel_dp->dp_iic_bus != NULL) { if (intel_dp->adapter != NULL) { device_delete_child(intel_dp->dp_iic_bus, intel_dp->adapter); } device_delete_child(dev->device, intel_dp->dp_iic_bus); } drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { struct drm_i915_private *dev_priv = intel_dp->base.base.dev->dev_private; taskqueue_cancel_timeout(dev_priv->tq, &intel_dp->panel_vdd_task, NULL); taskqueue_drain_timeout(dev_priv->tq, &intel_dp->panel_vdd_task); ironlake_panel_vdd_off_sync(intel_dp); } free(intel_dp, DRM_MEM_KMS); } static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .dpms = intel_dp_dpms, .mode_fixup = intel_dp_mode_fixup, .prepare = intel_dp_prepare, .mode_set = intel_dp_mode_set, .commit = intel_dp_commit, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = intel_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, .destroy = intel_dp_destroy, }; static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { .get_modes = intel_dp_get_modes, .mode_valid = intel_dp_mode_valid, .best_encoder = intel_best_encoder, }; static const struct drm_encoder_funcs intel_dp_enc_funcs = { .destroy = intel_dp_encoder_destroy, }; static void intel_dp_hot_plug(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); intel_dp_check_link_status(intel_dp); } /* Return which DP Port should be selected for Transcoder DP control */ int intel_trans_dp_port_sel(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_encoder *encoder; list_for_each_entry(encoder, &mode_config->encoder_list, head) { struct intel_dp *intel_dp; if (encoder->crtc != crtc) continue; intel_dp = enc_to_intel_dp(encoder); if (intel_dp->base.type == INTEL_OUTPUT_DISPLAYPORT || intel_dp->base.type == INTEL_OUTPUT_EDP) return intel_dp->output_reg; } return -1; } /* check the VBT to see whether the eDP is on DP-D port */ bool intel_dpd_is_edp(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct child_device_config *p_child; int i; if (!dev_priv->child_dev_num) return false; for (i = 0; i < dev_priv->child_dev_num; i++) { p_child = dev_priv->child_dev + i; if (p_child->dvo_port == PORT_IDPD && p_child->device_type == DEVICE_TYPE_eDP) return true; } return false; } static void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector) { intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); } void intel_dp_init(struct drm_device *dev, int output_reg) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector; struct intel_dp *intel_dp; struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; const char *name = NULL; int type; intel_dp = malloc(sizeof(struct intel_dp), DRM_MEM_KMS, M_WAITOK | M_ZERO); intel_dp->output_reg = output_reg; intel_dp->dpms_mode = -1; intel_connector = malloc(sizeof(struct intel_connector), DRM_MEM_KMS, M_WAITOK | M_ZERO); intel_encoder = &intel_dp->base; if (HAS_PCH_SPLIT(dev) && output_reg == PCH_DP_D) if (intel_dpd_is_edp(dev)) intel_dp->is_pch_edp = true; if (output_reg == DP_A || is_pch_edp(intel_dp)) { type = DRM_MODE_CONNECTOR_eDP; intel_encoder->type = INTEL_OUTPUT_EDP; } else { type = DRM_MODE_CONNECTOR_DisplayPort; intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; } connector = &intel_connector->base; drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_HPD; if (output_reg == DP_B || output_reg == PCH_DP_B) intel_encoder->clone_mask = (1 << INTEL_DP_B_CLONE_BIT); else if (output_reg == DP_C || output_reg == PCH_DP_C) intel_encoder->clone_mask = (1 << INTEL_DP_C_CLONE_BIT); else if (output_reg == DP_D || output_reg == PCH_DP_D) intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT); if (is_edp(intel_dp)) { intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT); TIMEOUT_TASK_INIT(dev_priv->tq, &intel_dp->panel_vdd_task, 0, ironlake_panel_vdd_work, intel_dp); } intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); connector->interlace_allowed = true; connector->doublescan_allowed = 0; drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs); intel_connector_attach_encoder(intel_connector, intel_encoder); #if 0 drm_sysfs_connector_add(connector); #endif /* Set up the DDC bus. */ switch (output_reg) { case DP_A: name = "DPDDC-A"; break; case DP_B: case PCH_DP_B: dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; name = "DPDDC-B"; break; case DP_C: case PCH_DP_C: dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; name = "DPDDC-C"; break; case DP_D: case PCH_DP_D: dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; name = "DPDDC-D"; break; } /* Cache some DPCD data in the eDP case */ if (is_edp(intel_dp)) { bool ret; struct edp_power_seq cur, vbt; u32 pp_on, pp_off, pp_div; pp_on = I915_READ(PCH_PP_ON_DELAYS); pp_off = I915_READ(PCH_PP_OFF_DELAYS); pp_div = I915_READ(PCH_PP_DIVISOR); if (!pp_on || !pp_off || !pp_div) { DRM_INFO("bad panel power sequencing delays, disabling panel\n"); intel_dp_encoder_destroy(&intel_dp->base.base); intel_dp_destroy(&intel_connector->base); return; } /* Pull timing values out of registers */ cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> PANEL_POWER_UP_DELAY_SHIFT; cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> PANEL_LIGHT_ON_DELAY_SHIFT; cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> PANEL_LIGHT_OFF_DELAY_SHIFT; cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> PANEL_POWER_DOWN_DELAY_SHIFT; cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000; DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); vbt = dev_priv->edp.pps; DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12); #define get_delay(field) ((max(cur.field, vbt.field) + 9) / 10) intel_dp->panel_power_up_delay = get_delay(t1_t3); intel_dp->backlight_on_delay = get_delay(t8); intel_dp->backlight_off_delay = get_delay(t9); intel_dp->panel_power_down_delay = get_delay(t10); intel_dp->panel_power_cycle_delay = get_delay(t11_t12); DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, intel_dp->panel_power_cycle_delay); DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); ironlake_edp_panel_vdd_on(intel_dp); ret = intel_dp_get_dpcd(intel_dp); ironlake_edp_panel_vdd_off(intel_dp, false); if (ret) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING; } else { /* if this fails, presume the device is a ghost */ DRM_INFO("failed to retrieve link info, disabling eDP\n"); intel_dp_encoder_destroy(&intel_dp->base.base); intel_dp_destroy(&intel_connector->base); return; } } intel_dp_i2c_init(intel_dp, intel_connector, name); intel_encoder->hot_plug = intel_dp_hot_plug; if (is_edp(intel_dp)) { dev_priv->int_edp_connector = connector; intel_panel_setup_backlight(dev); } intel_dp_add_properties(intel_dp, connector); /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written * 0xd. Failure to do so will result in spurious interrupts being * generated on the port when a cable is not attached. */ if (IS_G4X(dev) && !IS_GM45(dev)) { u32 temp = I915_READ(PEG_BAND_GAP_DATA); I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); } } Index: projects/building-blocks/sys/dev/drm2/i915/intel_iic.c =================================================================== --- projects/building-blocks/sys/dev/drm2/i915/intel_iic.c (revision 278195) +++ projects/building-blocks/sys/dev/drm2/i915/intel_iic.c (revision 278196) @@ -1,810 +1,816 @@ /* * Copyright (c) 2006 Dave Airlie * Copyright © 2006-2008,2010 Intel Corporation * Jesse Barnes * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (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 (including the next * paragraph) 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. * * Authors: * Eric Anholt * Chris Wilson * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * * This software was developed by Konstantin Belousov 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include "iicbb_if.h" static void intel_teardown_gmbus_m(struct drm_device *dev, int m); struct gmbus_port { const char *name; int reg; }; static const struct gmbus_port gmbus_ports[] = { { "ssc", GPIOB }, { "vga", GPIOA }, { "panel", GPIOC }, { "dpc", GPIOD }, { "dpb", GPIOE }, { "dpd", GPIOF }, }; /* Intel GPIO access functions */ #define I2C_RISEFALL_TIME 10 struct intel_iic_softc { struct drm_device *drm_dev; device_t iic_dev; bool force_bit_dev; char name[32]; uint32_t reg; uint32_t reg0; }; static void intel_iic_quirk_set(struct drm_i915_private *dev_priv, bool enable) { u32 val; /* When using bit bashing for I2C, this bit needs to be set to 1 */ if (!IS_PINEVIEW(dev_priv->dev)) return; val = I915_READ(DSPCLK_GATE_D); if (enable) val |= DPCUNIT_CLOCK_GATE_DISABLE; else val &= ~DPCUNIT_CLOCK_GATE_DISABLE; I915_WRITE(DSPCLK_GATE_D, val); } static u32 intel_iic_get_reserved(device_t idev) { struct intel_iic_softc *sc; struct drm_device *dev; struct drm_i915_private *dev_priv; u32 reserved; sc = device_get_softc(idev); dev = sc->drm_dev; dev_priv = dev->dev_private; if (!IS_I830(dev) && !IS_845G(dev)) { reserved = I915_READ_NOTRACE(sc->reg) & (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); } else { reserved = 0; } return (reserved); } void intel_iic_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv; dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); } static int intel_iicbus_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr) { struct intel_iic_softc *sc; struct drm_device *dev; sc = device_get_softc(idev); dev = sc->drm_dev; intel_iic_reset(dev); return (0); } static void intel_iicbb_setsda(device_t idev, int val) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; u32 reserved; u32 data_bits; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; reserved = intel_iic_get_reserved(idev); if (val) data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; else data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | GPIO_DATA_VAL_MASK; I915_WRITE_NOTRACE(sc->reg, reserved | data_bits); POSTING_READ(sc->reg); } static void intel_iicbb_setscl(device_t idev, int val) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; u32 clock_bits, reserved; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; reserved = intel_iic_get_reserved(idev); if (val) clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; else clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; I915_WRITE_NOTRACE(sc->reg, reserved | clock_bits); POSTING_READ(sc->reg); } static int intel_iicbb_getsda(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; u32 reserved; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; reserved = intel_iic_get_reserved(idev); I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_DATA_DIR_MASK); I915_WRITE_NOTRACE(sc->reg, reserved); return ((I915_READ_NOTRACE(sc->reg) & GPIO_DATA_VAL_IN) != 0); } static int intel_iicbb_getscl(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; u32 reserved; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; reserved = intel_iic_get_reserved(idev); I915_WRITE_NOTRACE(sc->reg, reserved | GPIO_CLOCK_DIR_MASK); I915_WRITE_NOTRACE(sc->reg, reserved); return ((I915_READ_NOTRACE(sc->reg) & GPIO_CLOCK_VAL_IN) != 0); } static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg, u32 gmbus1_index) { int reg_offset = dev_priv->gpio_mmio_base; u16 len = msg->len; u8 *buf = msg->buf; I915_WRITE(GMBUS1 + reg_offset, gmbus1_index | GMBUS_CYCLE_WAIT | (len << GMBUS_BYTE_COUNT_SHIFT) | (msg->slave << (GMBUS_SLAVE_ADDR_SHIFT - 1)) | GMBUS_SLAVE_READ | GMBUS_SW_RDY); while (len) { int ret; u32 val, loop = 0; u32 gmbus2; ret = _intel_wait_for(sc->drm_dev, ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & (GMBUS_SATOER | GMBUS_HW_RDY)), 50, 1, "915gbr"); if (ret) return (ETIMEDOUT); if (gmbus2 & GMBUS_SATOER) return (ENXIO); val = I915_READ(GMBUS3 + reg_offset); do { *buf++ = val & 0xff; val >>= 8; } while (--len != 0 && ++loop < 4); } return 0; } static int gmbus_xfer_write(struct drm_i915_private *dev_priv, struct iic_msg *msg) { int reg_offset = dev_priv->gpio_mmio_base; u16 len = msg->len; u8 *buf = msg->buf; u32 val, loop; val = loop = 0; while (len && loop < 4) { val |= *buf++ << (8 * loop++); len -= 1; } I915_WRITE(GMBUS3 + reg_offset, val); I915_WRITE(GMBUS1 + reg_offset, GMBUS_CYCLE_WAIT | (msg->len << GMBUS_BYTE_COUNT_SHIFT) | (msg->slave << (GMBUS_SLAVE_ADDR_SHIFT - 1)) | GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); while (len) { int ret; u32 gmbus2; val = loop = 0; do { val |= *buf++ << (8 * loop); } while (--len != 0 && ++loop < 4); I915_WRITE(GMBUS3 + reg_offset, val); ret = _intel_wait_for(sc->drm_dev, ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & (GMBUS_SATOER | GMBUS_HW_RDY)), 50, 1, "915gbw"); if (ret) return (ETIMEDOUT); if (gmbus2 & GMBUS_SATOER) return (ENXIO); } return 0; } /* * The gmbus controller can combine a 1 or 2 byte write with a read that * immediately follows it by using an "INDEX" cycle. */ static bool gmbus_is_index_read(struct iic_msg *msgs, int i, int num) { return (i + 1 < num && !(msgs[i].flags & IIC_M_RD) && msgs[i].len <= 2 && (msgs[i + 1].flags & IIC_M_RD)); } static int gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct iic_msg *msgs) { int reg_offset = dev_priv->gpio_mmio_base; u32 gmbus1_index = 0; u32 gmbus5 = 0; int ret; if (msgs[0].len == 2) gmbus5 = GMBUS_2BYTE_INDEX_EN | msgs[0].buf[1] | (msgs[0].buf[0] << 8); if (msgs[0].len == 1) gmbus1_index = GMBUS_CYCLE_INDEX | (msgs[0].buf[0] << GMBUS_SLAVE_INDEX_SHIFT); /* GMBUS5 holds 16-bit index */ if (gmbus5) I915_WRITE(GMBUS5 + reg_offset, gmbus5); ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index); /* Clear GMBUS5 after each index transfer */ if (gmbus5) I915_WRITE(GMBUS5 + reg_offset, 0); return ret; } static int intel_gmbus_transfer(device_t idev, struct iic_msg *msgs, uint32_t nmsgs) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; int error, i, ret, reg_offset, unit; error = 0; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; unit = device_get_unit(idev); sx_xlock(&dev_priv->gmbus_sx); if (sc->force_bit_dev) { error = IICBUS_TRANSFER(dev_priv->bbbus[unit], msgs, nmsgs); goto out; } reg_offset = dev_priv->gpio_mmio_base; I915_WRITE(GMBUS0 + reg_offset, sc->reg0); for (i = 0; i < nmsgs; i++) { u32 gmbus2; if (gmbus_is_index_read(msgs, i, nmsgs)) { error = gmbus_xfer_index_read(dev_priv, &msgs[i]); i += 1; /* set i to the index of the read xfer */ } else if (msgs[i].flags & IIC_M_RD) { error = gmbus_xfer_read(dev_priv, &msgs[i], 0); } else { error = gmbus_xfer_write(dev_priv, &msgs[i]); } if (error == ETIMEDOUT) goto timeout; if (error == ENXIO) goto clear_err; ret = _intel_wait_for(sc->drm_dev, ((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE)), 50, 1, "915gbh"); if (ret) goto timeout; if (gmbus2 & GMBUS_SATOER) goto clear_err; } /* Generate a STOP condition on the bus. Note that gmbus can't generata * a STOP on the very first cycle. To simplify the code we * unconditionally generate the STOP condition with an additional gmbus * cycle. */ I915_WRITE(GMBUS1 + reg_offset, GMBUS_CYCLE_STOP | GMBUS_SW_RDY); /* Mark the GMBUS interface as disabled after waiting for idle. * We will re-enable it at the start of the next xfer, * till then let it sleep. */ if (_intel_wait_for(dev, (I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, 10, 1, "915gbu")) { DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n", sc->name); error = ETIMEDOUT; } I915_WRITE(GMBUS0 + reg_offset, 0); goto out; clear_err: /* * Wait for bus to IDLE before clearing NAK. * If we clear the NAK while bus is still active, then it will stay * active and the next transaction may fail. */ if (_intel_wait_for(dev, (I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, 10, 1, "915gbu")) DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n", sc->name); /* Toggle the Software Clear Interrupt bit. This has the effect * of resetting the GMBUS controller and so clearing the * BUS_ERROR raised by the slave's NAK. */ I915_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT); I915_WRITE(GMBUS1 + reg_offset, 0); I915_WRITE(GMBUS0 + reg_offset, 0); DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n", sc->name, msgs[i].slave, (msgs[i].flags & IIC_M_RD) ? 'r' : 'w', msgs[i].len); /* * If no ACK is received during the address phase of a transaction, * the adapter must report -ENXIO. * It is not clear what to return if no ACK is received at other times. * So, we always return -ENXIO in all NAK cases, to ensure we send * it at least during the one case that is specified. */ error = ENXIO; goto out; timeout: DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n", sc->name, sc->reg0 & 0xff); I915_WRITE(GMBUS0 + reg_offset, 0); /* * Hardware may not support GMBUS over these pins? * Try GPIO bitbanging instead. */ sc->force_bit_dev = true; error = IICBUS_TRANSFER(idev, msgs, nmsgs); goto out; out: sx_xunlock(&dev_priv->gmbus_sx); return (error); } device_t intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned port) { if (!intel_gmbus_is_port_valid(port)) DRM_ERROR("GMBUS get adapter %d: invalid port\n", port); return (intel_gmbus_is_port_valid(port) ? dev_priv->gmbus[port - 1] : NULL); } void intel_gmbus_set_speed(device_t idev, int speed) { struct intel_iic_softc *sc; sc = device_get_softc(device_get_parent(idev)); sc->reg0 = (sc->reg0 & ~(0x3 << 8)) | speed; } void intel_gmbus_force_bit(device_t idev, bool force_bit) { struct intel_iic_softc *sc; sc = device_get_softc(device_get_parent(idev)); sc->force_bit_dev = force_bit; } static int intel_iicbb_pre_xfer(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; intel_iic_reset(sc->drm_dev); intel_iic_quirk_set(dev_priv, true); IICBB_SETSDA(idev, 1); IICBB_SETSCL(idev, 1); DELAY(I2C_RISEFALL_TIME); return (0); } static void intel_iicbb_post_xfer(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; sc = device_get_softc(idev); dev_priv = sc->drm_dev->dev_private; IICBB_SETSDA(idev, 1); IICBB_SETSCL(idev, 1); intel_iic_quirk_set(dev_priv, false); } static int intel_gmbus_probe(device_t dev) { return (BUS_PROBE_SPECIFIC); } static int intel_gmbus_attach(device_t idev) { struct drm_i915_private *dev_priv; struct intel_iic_softc *sc; int pin, port; sc = device_get_softc(idev); sc->drm_dev = device_get_softc(device_get_parent(idev)); dev_priv = sc->drm_dev->dev_private; pin = device_get_unit(idev); port = pin + 1; - snprintf(sc->name, sizeof(sc->name), "gmbus %s", gmbus_ports[pin].name); + snprintf(sc->name, sizeof(sc->name), "gmbus %s", + intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name : + "reserved"); device_set_desc(idev, sc->name); /* By default use a conservative clock rate */ sc->reg0 = port | GMBUS_RATE_100KHZ; /* gmbus seems to be broken on i830 */ if (IS_I830(sc->drm_dev)) sc->force_bit_dev = true; #if 0 if (IS_GEN2(sc->drm_dev)) { sc->force_bit_dev = true; } #endif /* add bus interface device */ sc->iic_dev = device_add_child(idev, "iicbus", -1); if (sc->iic_dev == NULL) return (ENXIO); device_quiet(sc->iic_dev); bus_generic_attach(idev); return (0); } static int intel_gmbus_detach(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; device_t child; int u; sc = device_get_softc(idev); u = device_get_unit(idev); dev_priv = sc->drm_dev->dev_private; child = sc->iic_dev; bus_generic_detach(idev); if (child != NULL) device_delete_child(idev, child); return (0); } static int intel_iicbb_probe(device_t dev) { return (BUS_PROBE_DEFAULT); } static int intel_iicbb_attach(device_t idev) { struct intel_iic_softc *sc; struct drm_i915_private *dev_priv; - int pin; + int pin, port; sc = device_get_softc(idev); sc->drm_dev = device_get_softc(device_get_parent(idev)); dev_priv = sc->drm_dev->dev_private; pin = device_get_unit(idev); + port = pin + 1; snprintf(sc->name, sizeof(sc->name), "i915 iicbb %s", - gmbus_ports[pin].name); + intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name : + "reserved"); device_set_desc(idev, sc->name); + if (!intel_gmbus_is_port_valid(port)) + pin = 1 ; /* GPIOA, VGA */ sc->reg0 = pin | GMBUS_RATE_100KHZ; sc->reg = dev_priv->gpio_mmio_base + gmbus_ports[pin].reg; /* add generic bit-banging code */ sc->iic_dev = device_add_child(idev, "iicbb", -1); if (sc->iic_dev == NULL) return (ENXIO); device_quiet(sc->iic_dev); bus_generic_attach(idev); iicbus_set_nostop(idev, true); return (0); } static int intel_iicbb_detach(device_t idev) { struct intel_iic_softc *sc; device_t child; sc = device_get_softc(idev); child = sc->iic_dev; bus_generic_detach(idev); if (child) device_delete_child(idev, child); return (0); } static device_method_t intel_gmbus_methods[] = { DEVMETHOD(device_probe, intel_gmbus_probe), DEVMETHOD(device_attach, intel_gmbus_attach), DEVMETHOD(device_detach, intel_gmbus_detach), DEVMETHOD(iicbus_reset, intel_iicbus_reset), DEVMETHOD(iicbus_transfer, intel_gmbus_transfer), DEVMETHOD_END }; static driver_t intel_gmbus_driver = { "intel_gmbus", intel_gmbus_methods, sizeof(struct intel_iic_softc) }; static devclass_t intel_gmbus_devclass; DRIVER_MODULE_ORDERED(intel_gmbus, drmn, intel_gmbus_driver, intel_gmbus_devclass, 0, 0, SI_ORDER_FIRST); DRIVER_MODULE(iicbus, intel_gmbus, iicbus_driver, iicbus_devclass, 0, 0); static device_method_t intel_iicbb_methods[] = { DEVMETHOD(device_probe, intel_iicbb_probe), DEVMETHOD(device_attach, intel_iicbb_attach), DEVMETHOD(device_detach, intel_iicbb_detach), DEVMETHOD(bus_add_child, bus_generic_add_child), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(iicbb_callback, iicbus_null_callback), DEVMETHOD(iicbb_reset, intel_iicbus_reset), DEVMETHOD(iicbb_setsda, intel_iicbb_setsda), DEVMETHOD(iicbb_setscl, intel_iicbb_setscl), DEVMETHOD(iicbb_getsda, intel_iicbb_getsda), DEVMETHOD(iicbb_getscl, intel_iicbb_getscl), DEVMETHOD(iicbb_pre_xfer, intel_iicbb_pre_xfer), DEVMETHOD(iicbb_post_xfer, intel_iicbb_post_xfer), DEVMETHOD_END }; static driver_t intel_iicbb_driver = { "intel_iicbb", intel_iicbb_methods, sizeof(struct intel_iic_softc) }; static devclass_t intel_iicbb_devclass; DRIVER_MODULE_ORDERED(intel_iicbb, drmn, intel_iicbb_driver, intel_iicbb_devclass, 0, 0, SI_ORDER_FIRST); DRIVER_MODULE(iicbb, intel_iicbb, iicbb_driver, iicbb_devclass, 0, 0); int intel_setup_gmbus(struct drm_device *dev) { struct drm_i915_private *dev_priv; device_t iic_dev; int i, ret; dev_priv = dev->dev_private; sx_init(&dev_priv->gmbus_sx, "gmbus"); if (HAS_PCH_SPLIT(dev)) dev_priv->gpio_mmio_base = PCH_GPIOA - GPIOA; else dev_priv->gpio_mmio_base = 0; /* * The Giant there is recursed, most likely. Normally, the * intel_setup_gmbus() is called from the attach method of the * driver. */ mtx_lock(&Giant); for (i = 0; i <= GMBUS_NUM_PORTS; i++) { /* * Initialized bbbus_bridge before gmbus_bridge, since * gmbus may decide to force quirk transfer in the * attachment code. */ dev_priv->bbbus_bridge[i] = device_add_child(dev->device, "intel_iicbb", i); if (dev_priv->bbbus_bridge[i] == NULL) { DRM_ERROR("bbbus bridge %d creation failed\n", i); ret = ENXIO; goto err; } device_quiet(dev_priv->bbbus_bridge[i]); ret = device_probe_and_attach(dev_priv->bbbus_bridge[i]); if (ret != 0) { DRM_ERROR("bbbus bridge %d attach failed, %d\n", i, ret); goto err; } iic_dev = device_find_child(dev_priv->bbbus_bridge[i], "iicbb", -1); if (iic_dev == NULL) { DRM_ERROR("bbbus bridge doesn't have iicbb child\n"); goto err; } iic_dev = device_find_child(iic_dev, "iicbus", -1); if (iic_dev == NULL) { DRM_ERROR( "bbbus bridge doesn't have iicbus grandchild\n"); goto err; } dev_priv->bbbus[i] = iic_dev; dev_priv->gmbus_bridge[i] = device_add_child(dev->device, "intel_gmbus", i); if (dev_priv->gmbus_bridge[i] == NULL) { DRM_ERROR("gmbus bridge %d creation failed\n", i); ret = ENXIO; goto err; } device_quiet(dev_priv->gmbus_bridge[i]); ret = device_probe_and_attach(dev_priv->gmbus_bridge[i]); if (ret != 0) { DRM_ERROR("gmbus bridge %d attach failed, %d\n", i, ret); ret = ENXIO; goto err; } iic_dev = device_find_child(dev_priv->gmbus_bridge[i], "iicbus", -1); if (iic_dev == NULL) { DRM_ERROR("gmbus bridge doesn't have iicbus child\n"); goto err; } dev_priv->gmbus[i] = iic_dev; intel_iic_reset(dev); } mtx_unlock(&Giant); return (0); err: intel_teardown_gmbus_m(dev, i); mtx_unlock(&Giant); return (ret); } static void intel_teardown_gmbus_m(struct drm_device *dev, int m) { struct drm_i915_private *dev_priv; dev_priv = dev->dev_private; sx_destroy(&dev_priv->gmbus_sx); } void intel_teardown_gmbus(struct drm_device *dev) { mtx_lock(&Giant); intel_teardown_gmbus_m(dev, GMBUS_NUM_PORTS); mtx_unlock(&Giant); } Index: projects/building-blocks/sys/dev/drm2/ttm/ttm_bo.c =================================================================== --- projects/building-blocks/sys/dev/drm2/ttm/ttm_bo.c (revision 278195) +++ projects/building-blocks/sys/dev/drm2/ttm/ttm_bo.c (revision 278196) @@ -1,1887 +1,1896 @@ /************************************************************************** * * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, 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 (including the * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. * **************************************************************************/ /* * Authors: Thomas Hellstrom */ #include __FBSDID("$FreeBSD$"); #include #include #include #include +#include #define TTM_ASSERT_LOCKED(param) #define TTM_DEBUG(fmt, arg...) #define TTM_BO_HASH_ORDER 13 static int ttm_bo_setup_vm(struct ttm_buffer_object *bo); static int ttm_bo_swapout(struct ttm_mem_shrink *shrink); static void ttm_bo_global_kobj_release(struct ttm_bo_global *glob); MALLOC_DEFINE(M_TTM_BO, "ttm_bo", "TTM Buffer Objects"); static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type) { int i; for (i = 0; i <= TTM_PL_PRIV5; i++) if (flags & (1 << i)) { *mem_type = i; return 0; } return -EINVAL; } static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; printf(" has_type: %d\n", man->has_type); printf(" use_type: %d\n", man->use_type); printf(" flags: 0x%08X\n", man->flags); printf(" gpu_offset: 0x%08lX\n", man->gpu_offset); printf(" size: %ju\n", (uintmax_t)man->size); printf(" available_caching: 0x%08X\n", man->available_caching); printf(" default_caching: 0x%08X\n", man->default_caching); if (mem_type != TTM_PL_SYSTEM) (*man->func->debug)(man, TTM_PFX); } static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, struct ttm_placement *placement) { int i, ret, mem_type; printf("No space for %p (%lu pages, %luK, %luM)\n", bo, bo->mem.num_pages, bo->mem.size >> 10, bo->mem.size >> 20); for (i = 0; i < placement->num_placement; i++) { ret = ttm_mem_type_from_flags(placement->placement[i], &mem_type); if (ret) return; printf(" placement[%d]=0x%08X (%d)\n", i, placement->placement[i], mem_type); ttm_mem_type_debug(bo->bdev, mem_type); } } #if 0 static ssize_t ttm_bo_global_show(struct ttm_bo_global *glob, char *buffer) { return snprintf(buffer, PAGE_SIZE, "%lu\n", (unsigned long) atomic_read(&glob->bo_count)); } #endif static inline uint32_t ttm_bo_type_flags(unsigned type) { return 1 << (type); } static void ttm_bo_release_list(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; size_t acc_size = bo->acc_size; MPASS(atomic_read(&bo->list_kref) == 0); MPASS(atomic_read(&bo->kref) == 0); MPASS(atomic_read(&bo->cpu_writers) == 0); MPASS(bo->sync_obj == NULL); MPASS(bo->mem.mm_node == NULL); MPASS(list_empty(&bo->lru)); MPASS(list_empty(&bo->ddestroy)); if (bo->ttm) ttm_tt_destroy(bo->ttm); atomic_dec(&bo->glob->bo_count); if (bo->destroy) bo->destroy(bo); else { free(bo, M_TTM_BO); } ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } static int ttm_bo_wait_unreserved_locked(struct ttm_buffer_object *bo, bool interruptible) { const char *wmsg; int flags, ret; ret = 0; if (interruptible) { flags = PCATCH; wmsg = "ttbowi"; } else { flags = 0; wmsg = "ttbowu"; } while (ttm_bo_is_reserved(bo)) { ret = -msleep(bo, &bo->glob->lru_lock, flags, wmsg, 0); if (ret == -EINTR) ret = -ERESTARTSYS; if (ret != 0) break; } return (ret); } void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man; MPASS(ttm_bo_is_reserved(bo)); if (!(bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) { MPASS(list_empty(&bo->lru)); man = &bdev->man[bo->mem.mem_type]; list_add_tail(&bo->lru, &man->lru); refcount_acquire(&bo->list_kref); if (bo->ttm != NULL) { list_add_tail(&bo->swap, &bo->glob->swap_lru); refcount_acquire(&bo->list_kref); } } } int ttm_bo_del_from_lru(struct ttm_buffer_object *bo) { int put_count = 0; if (!list_empty(&bo->swap)) { list_del_init(&bo->swap); ++put_count; } if (!list_empty(&bo->lru)) { list_del_init(&bo->lru); ++put_count; } /* * TODO: Add a driver hook to delete from * driver-specific LRU's here. */ return put_count; } int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { int ret; while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { /** * Deadlock avoidance for multi-bo reserving. */ if (use_sequence && bo->seq_valid) { /** * We've already reserved this one. */ if (unlikely(sequence == bo->val_seq)) return -EDEADLK; /** * Already reserved by a thread that will not back * off for us. We need to back off. */ if (unlikely(sequence - bo->val_seq < (1U << 31))) return -EAGAIN; } if (no_wait) return -EBUSY; ret = ttm_bo_wait_unreserved_locked(bo, interruptible); if (unlikely(ret)) return ret; } if (use_sequence) { bool wake_up = false; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ if (unlikely((bo->val_seq - sequence < (1U << 31)) || !bo->seq_valid)) wake_up = true; /* * In the worst case with memory ordering these values can be * seen in the wrong order. However since we call wake_up_all * in that case, this will hopefully not pose a problem, * and the worst case would only cause someone to accidentally * hit -EAGAIN in ttm_bo_reserve when they see old value of * val_seq. However this would only happen if seq_valid was * written before val_seq was, and just means some slightly * increased cpu usage */ bo->val_seq = sequence; bo->seq_valid = true; if (wake_up) wakeup(bo); } else { bo->seq_valid = false; } return 0; } void ttm_bo_list_ref_sub(struct ttm_buffer_object *bo, int count, bool never_free) { u_int old; old = atomic_fetchadd_int(&bo->list_kref, -count); if (old <= count) { if (never_free) panic("ttm_bo_ref_buf"); ttm_bo_release_list(bo); } } int ttm_bo_reserve(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { struct ttm_bo_global *glob = bo->glob; int put_count = 0; int ret; mtx_lock(&bo->glob->lru_lock); ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence, sequence); if (likely(ret == 0)) { put_count = ttm_bo_del_from_lru(bo); mtx_unlock(&glob->lru_lock); ttm_bo_list_ref_sub(bo, put_count, true); } else mtx_unlock(&bo->glob->lru_lock); return ret; } int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, bool interruptible, uint32_t sequence) { bool wake_up = false; int ret; while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { if (bo->seq_valid && sequence == bo->val_seq) { DRM_ERROR( "%s: bo->seq_valid && sequence == bo->val_seq", __func__); } ret = ttm_bo_wait_unreserved_locked(bo, interruptible); if (unlikely(ret)) return ret; } if ((bo->val_seq - sequence < (1U << 31)) || !bo->seq_valid) wake_up = true; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ bo->val_seq = sequence; bo->seq_valid = true; if (wake_up) wakeup(bo); return 0; } int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, bool interruptible, uint32_t sequence) { struct ttm_bo_global *glob = bo->glob; int put_count, ret; mtx_lock(&glob->lru_lock); ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence); if (likely(!ret)) { put_count = ttm_bo_del_from_lru(bo); mtx_unlock(&glob->lru_lock); ttm_bo_list_ref_sub(bo, put_count, true); } else mtx_unlock(&glob->lru_lock); return ret; } void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo) { ttm_bo_add_to_lru(bo); atomic_set(&bo->reserved, 0); wakeup(bo); } void ttm_bo_unreserve(struct ttm_buffer_object *bo) { struct ttm_bo_global *glob = bo->glob; mtx_lock(&glob->lru_lock); ttm_bo_unreserve_locked(bo); mtx_unlock(&glob->lru_lock); } /* * Call bo->mutex locked. */ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_global *glob = bo->glob; int ret = 0; uint32_t page_flags = 0; TTM_ASSERT_LOCKED(&bo->mutex); bo->ttm = NULL; if (bdev->need_dma32) page_flags |= TTM_PAGE_FLAG_DMA32; switch (bo->type) { case ttm_bo_type_device: if (zero_alloc) page_flags |= TTM_PAGE_FLAG_ZERO_ALLOC; case ttm_bo_type_kernel: bo->ttm = bdev->driver->ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, page_flags, glob->dummy_read_page); if (unlikely(bo->ttm == NULL)) ret = -ENOMEM; break; case ttm_bo_type_sg: bo->ttm = bdev->driver->ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT, page_flags | TTM_PAGE_FLAG_SG, glob->dummy_read_page); if (unlikely(bo->ttm == NULL)) { ret = -ENOMEM; break; } bo->ttm->sg = bo->sg; break; default: printf("[TTM] Illegal buffer object type\n"); ret = -EINVAL; break; } return ret; } static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem, bool evict, bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; bool old_is_pci = ttm_mem_reg_is_pci(bdev, &bo->mem); bool new_is_pci = ttm_mem_reg_is_pci(bdev, mem); struct ttm_mem_type_manager *old_man = &bdev->man[bo->mem.mem_type]; struct ttm_mem_type_manager *new_man = &bdev->man[mem->mem_type]; int ret = 0; if (old_is_pci || new_is_pci || ((mem->placement & bo->mem.placement & TTM_PL_MASK_CACHING) == 0)) { ret = ttm_mem_io_lock(old_man, true); if (unlikely(ret != 0)) goto out_err; ttm_bo_unmap_virtual_locked(bo); ttm_mem_io_unlock(old_man); } /* * Create and bind a ttm if required. */ if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) { if (bo->ttm == NULL) { bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED); ret = ttm_bo_add_ttm(bo, zero); if (ret) goto out_err; } ret = ttm_tt_set_placement_caching(bo->ttm, mem->placement); if (ret) goto out_err; if (mem->mem_type != TTM_PL_SYSTEM) { ret = ttm_tt_bind(bo->ttm, mem); if (ret) goto out_err; } if (bo->mem.mem_type == TTM_PL_SYSTEM) { if (bdev->driver->move_notify) bdev->driver->move_notify(bo, mem); bo->mem = *mem; mem->mm_node = NULL; goto moved; } } if (bdev->driver->move_notify) bdev->driver->move_notify(bo, mem); if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) ret = ttm_bo_move_ttm(bo, evict, no_wait_gpu, mem); else if (bdev->driver->move) ret = bdev->driver->move(bo, evict, interruptible, no_wait_gpu, mem); else ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, mem); if (ret) { if (bdev->driver->move_notify) { struct ttm_mem_reg tmp_mem = *mem; *mem = bo->mem; bo->mem = tmp_mem; bdev->driver->move_notify(bo, mem); bo->mem = *mem; *mem = tmp_mem; } goto out_err; } moved: if (bo->evicted) { ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); if (ret) printf("[TTM] Can not flush read caches\n"); bo->evicted = false; } if (bo->mem.mm_node) { bo->offset = (bo->mem.start << PAGE_SHIFT) + bdev->man[bo->mem.mem_type].gpu_offset; bo->cur_placement = bo->mem.placement; } else bo->offset = 0; return 0; out_err: new_man = &bdev->man[bo->mem.mem_type]; if ((new_man->flags & TTM_MEMTYPE_FLAG_FIXED) && bo->ttm) { ttm_tt_unbind(bo->ttm); ttm_tt_destroy(bo->ttm); bo->ttm = NULL; } return ret; } /** * Call bo::reserved. * Will release GPU memory type usage on destruction. * This is the place to put in driver specific hooks to release * driver private resources. * Will release the bo::reserved lock. */ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) { if (bo->bdev->driver->move_notify) bo->bdev->driver->move_notify(bo, NULL); if (bo->ttm) { ttm_tt_unbind(bo->ttm); ttm_tt_destroy(bo->ttm); bo->ttm = NULL; } ttm_bo_mem_put(bo, &bo->mem); atomic_set(&bo->reserved, 0); wakeup(&bo); /* * Since the final reference to this bo may not be dropped by * the current task we have to put a memory barrier here to make * sure the changes done in this function are always visible. * * This function only needs protection against the final kref_put. */ mb(); } static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_global *glob = bo->glob; struct ttm_bo_driver *driver = bdev->driver; void *sync_obj = NULL; int put_count; int ret; mtx_lock(&glob->lru_lock); ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); mtx_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); if (!ret && !bo->sync_obj) { mtx_unlock(&bdev->fence_lock); put_count = ttm_bo_del_from_lru(bo); mtx_unlock(&glob->lru_lock); ttm_bo_cleanup_memtype_use(bo); ttm_bo_list_ref_sub(bo, put_count, true); return; } if (bo->sync_obj) sync_obj = driver->sync_obj_ref(bo->sync_obj); mtx_unlock(&bdev->fence_lock); if (!ret) { atomic_set(&bo->reserved, 0); wakeup(bo); } refcount_acquire(&bo->list_kref); list_add_tail(&bo->ddestroy, &bdev->ddestroy); mtx_unlock(&glob->lru_lock); if (sync_obj) { driver->sync_obj_flush(sync_obj); driver->sync_obj_unref(&sync_obj); } taskqueue_enqueue_timeout(taskqueue_thread, &bdev->wq, ((hz / 100) < 1) ? 1 : hz / 100); } /** * function ttm_bo_cleanup_refs_and_unlock * If bo idle, remove from delayed- and lru lists, and unref. * If not idle, do nothing. * * Must be called with lru_lock and reservation held, this function * will drop both before returning. * * @interruptible Any sleeps should occur interruptibly. * @no_wait_gpu Never wait for gpu. Return -EBUSY instead. */ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_driver *driver = bdev->driver; struct ttm_bo_global *glob = bo->glob; int put_count; int ret; mtx_lock(&bdev->fence_lock); ret = ttm_bo_wait(bo, false, false, true); if (ret && !no_wait_gpu) { void *sync_obj; /* * Take a reference to the fence and unreserve, * at this point the buffer should be dead, so * no new sync objects can be attached. */ sync_obj = driver->sync_obj_ref(bo->sync_obj); mtx_unlock(&bdev->fence_lock); atomic_set(&bo->reserved, 0); wakeup(bo); mtx_unlock(&glob->lru_lock); ret = driver->sync_obj_wait(sync_obj, false, interruptible); driver->sync_obj_unref(&sync_obj); if (ret) return ret; /* * remove sync_obj with ttm_bo_wait, the wait should be * finished, and no new wait object should have been added. */ mtx_lock(&bdev->fence_lock); ret = ttm_bo_wait(bo, false, false, true); mtx_unlock(&bdev->fence_lock); if (ret) return ret; mtx_lock(&glob->lru_lock); ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); /* * We raced, and lost, someone else holds the reservation now, * and is probably busy in ttm_bo_cleanup_memtype_use. * * Even if it's not the case, because we finished waiting any * delayed destruction would succeed, so just return success * here. */ if (ret) { mtx_unlock(&glob->lru_lock); return 0; } } else mtx_unlock(&bdev->fence_lock); if (ret || unlikely(list_empty(&bo->ddestroy))) { atomic_set(&bo->reserved, 0); wakeup(bo); mtx_unlock(&glob->lru_lock); return ret; } put_count = ttm_bo_del_from_lru(bo); list_del_init(&bo->ddestroy); ++put_count; mtx_unlock(&glob->lru_lock); ttm_bo_cleanup_memtype_use(bo); ttm_bo_list_ref_sub(bo, put_count, true); return 0; } /** * Traverse the delayed list, and call ttm_bo_cleanup_refs on all * encountered buffers. */ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) { struct ttm_bo_global *glob = bdev->glob; struct ttm_buffer_object *entry = NULL; int ret = 0; mtx_lock(&glob->lru_lock); if (list_empty(&bdev->ddestroy)) goto out_unlock; entry = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object, ddestroy); refcount_acquire(&entry->list_kref); for (;;) { struct ttm_buffer_object *nentry = NULL; if (entry->ddestroy.next != &bdev->ddestroy) { nentry = list_first_entry(&entry->ddestroy, struct ttm_buffer_object, ddestroy); refcount_acquire(&nentry->list_kref); } ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); if (remove_all && ret) { ret = ttm_bo_reserve_nolru(entry, false, false, false, 0); } if (!ret) ret = ttm_bo_cleanup_refs_and_unlock(entry, false, !remove_all); else mtx_unlock(&glob->lru_lock); if (refcount_release(&entry->list_kref)) ttm_bo_release_list(entry); entry = nentry; if (ret || !entry) goto out; mtx_lock(&glob->lru_lock); if (list_empty(&entry->ddestroy)) break; } out_unlock: mtx_unlock(&glob->lru_lock); out: if (entry && refcount_release(&entry->list_kref)) ttm_bo_release_list(entry); return ret; } static void ttm_bo_delayed_workqueue(void *arg, int pending __unused) { struct ttm_bo_device *bdev = arg; if (ttm_bo_delayed_delete(bdev, false)) { taskqueue_enqueue_timeout(taskqueue_thread, &bdev->wq, ((hz / 100) < 1) ? 1 : hz / 100); } } static void ttm_bo_release(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; rw_wlock(&bdev->vm_lock); if (likely(bo->vm_node != NULL)) { RB_REMOVE(ttm_bo_device_buffer_objects, &bdev->addr_space_rb, bo); drm_mm_put_block(bo->vm_node); bo->vm_node = NULL; } rw_wunlock(&bdev->vm_lock); ttm_mem_io_lock(man, false); ttm_mem_io_free_vm(bo); ttm_mem_io_unlock(man); ttm_bo_cleanup_refs_or_queue(bo); if (refcount_release(&bo->list_kref)) ttm_bo_release_list(bo); } void ttm_bo_unref(struct ttm_buffer_object **p_bo) { struct ttm_buffer_object *bo = *p_bo; *p_bo = NULL; if (refcount_release(&bo->kref)) ttm_bo_release(bo); } int ttm_bo_lock_delayed_workqueue(struct ttm_bo_device *bdev) { int pending; taskqueue_cancel_timeout(taskqueue_thread, &bdev->wq, &pending); if (pending) taskqueue_drain_timeout(taskqueue_thread, &bdev->wq); return (pending); } void ttm_bo_unlock_delayed_workqueue(struct ttm_bo_device *bdev, int resched) { if (resched) { taskqueue_enqueue_timeout(taskqueue_thread, &bdev->wq, ((hz / 100) < 1) ? 1 : hz / 100); } } static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_reg evict_mem; struct ttm_placement placement; int ret = 0; mtx_lock(&bdev->fence_lock); ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu); mtx_unlock(&bdev->fence_lock); if (unlikely(ret != 0)) { if (ret != -ERESTART) { printf("[TTM] Failed to expire sync object before buffer eviction\n"); } goto out; } MPASS(ttm_bo_is_reserved(bo)); evict_mem = bo->mem; evict_mem.mm_node = NULL; evict_mem.bus.io_reserved_vm = false; evict_mem.bus.io_reserved_count = 0; placement.fpfn = 0; placement.lpfn = 0; placement.num_placement = 0; placement.num_busy_placement = 0; bdev->driver->evict_flags(bo, &placement); ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible, no_wait_gpu); if (ret) { if (ret != -ERESTART) { printf("[TTM] Failed to find memory space for buffer 0x%p eviction\n", bo); ttm_bo_mem_space_debug(bo, &placement); } goto out; } ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible, no_wait_gpu); if (ret) { if (ret != -ERESTART) printf("[TTM] Buffer eviction failed\n"); ttm_bo_mem_put(bo, &evict_mem); goto out; } bo->evicted = true; out: return ret; } static int ttm_mem_evict_first(struct ttm_bo_device *bdev, uint32_t mem_type, bool interruptible, bool no_wait_gpu) { struct ttm_bo_global *glob = bdev->glob; struct ttm_mem_type_manager *man = &bdev->man[mem_type]; struct ttm_buffer_object *bo; int ret = -EBUSY, put_count; mtx_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } if (ret) { mtx_unlock(&glob->lru_lock); return ret; } refcount_acquire(&bo->list_kref); if (!list_empty(&bo->ddestroy)) { ret = ttm_bo_cleanup_refs_and_unlock(bo, interruptible, no_wait_gpu); if (refcount_release(&bo->list_kref)) ttm_bo_release_list(bo); return ret; } put_count = ttm_bo_del_from_lru(bo); mtx_unlock(&glob->lru_lock); MPASS(ret == 0); ttm_bo_list_ref_sub(bo, put_count, true); ret = ttm_bo_evict(bo, interruptible, no_wait_gpu); ttm_bo_unreserve(bo); if (refcount_release(&bo->list_kref)) ttm_bo_release_list(bo); return ret; } void ttm_bo_mem_put(struct ttm_buffer_object *bo, struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bo->bdev->man[mem->mem_type]; if (mem->mm_node) (*man->func->put_node)(man, mem); } /** * Repeatedly evict memory from the LRU for @mem_type until we create enough * space, or we've evicted everything and there isn't enough space. */ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, uint32_t mem_type, struct ttm_placement *placement, struct ttm_mem_reg *mem, bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[mem_type]; int ret; do { ret = (*man->func->get_node)(man, bo, placement, mem); if (unlikely(ret != 0)) return ret; if (mem->mm_node) break; ret = ttm_mem_evict_first(bdev, mem_type, interruptible, no_wait_gpu); if (unlikely(ret != 0)) return ret; } while (1); if (mem->mm_node == NULL) return -ENOMEM; mem->mem_type = mem_type; return 0; } static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man, uint32_t cur_placement, uint32_t proposed_placement) { uint32_t caching = proposed_placement & TTM_PL_MASK_CACHING; uint32_t result = proposed_placement & ~TTM_PL_MASK_CACHING; /** * Keep current caching if possible. */ if ((cur_placement & caching) != 0) result |= (cur_placement & caching); else if ((man->default_caching & caching) != 0) result |= man->default_caching; else if ((TTM_PL_FLAG_CACHED & caching) != 0) result |= TTM_PL_FLAG_CACHED; else if ((TTM_PL_FLAG_WC & caching) != 0) result |= TTM_PL_FLAG_WC; else if ((TTM_PL_FLAG_UNCACHED & caching) != 0) result |= TTM_PL_FLAG_UNCACHED; return result; } static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man, uint32_t mem_type, uint32_t proposed_placement, uint32_t *masked_placement) { uint32_t cur_flags = ttm_bo_type_flags(mem_type); if ((cur_flags & proposed_placement & TTM_PL_MASK_MEM) == 0) return false; if ((proposed_placement & man->available_caching) == 0) return false; cur_flags |= (proposed_placement & man->available_caching); *masked_placement = cur_flags; return true; } /** * Creates space for memory region @mem according to its type. * * This function first searches for free space in compatible memory types in * the priority order defined by the driver. If free space isn't found, then * ttm_bo_mem_force_space is attempted in priority order to evict and find * space. */ int ttm_bo_mem_space(struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_mem_reg *mem, bool interruptible, bool no_wait_gpu) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man; uint32_t mem_type = TTM_PL_SYSTEM; uint32_t cur_flags = 0; bool type_found = false; bool type_ok = false; bool has_erestartsys = false; int i, ret; mem->mm_node = NULL; for (i = 0; i < placement->num_placement; ++i) { ret = ttm_mem_type_from_flags(placement->placement[i], &mem_type); if (ret) return ret; man = &bdev->man[mem_type]; type_ok = ttm_bo_mt_compatible(man, mem_type, placement->placement[i], &cur_flags); if (!type_ok) continue; cur_flags = ttm_bo_select_caching(man, bo->mem.placement, cur_flags); /* * Use the access and other non-mapping-related flag bits from * the memory placement flags to the current flags */ ttm_flag_masked(&cur_flags, placement->placement[i], ~TTM_PL_MASK_MEMTYPE); if (mem_type == TTM_PL_SYSTEM) break; if (man->has_type && man->use_type) { type_found = true; ret = (*man->func->get_node)(man, bo, placement, mem); if (unlikely(ret)) return ret; } if (mem->mm_node) break; } if ((type_ok && (mem_type == TTM_PL_SYSTEM)) || mem->mm_node) { mem->mem_type = mem_type; mem->placement = cur_flags; return 0; } if (!type_found) return -EINVAL; for (i = 0; i < placement->num_busy_placement; ++i) { ret = ttm_mem_type_from_flags(placement->busy_placement[i], &mem_type); if (ret) return ret; man = &bdev->man[mem_type]; if (!man->has_type) continue; if (!ttm_bo_mt_compatible(man, mem_type, placement->busy_placement[i], &cur_flags)) continue; cur_flags = ttm_bo_select_caching(man, bo->mem.placement, cur_flags); /* * Use the access and other non-mapping-related flag bits from * the memory placement flags to the current flags */ ttm_flag_masked(&cur_flags, placement->busy_placement[i], ~TTM_PL_MASK_MEMTYPE); if (mem_type == TTM_PL_SYSTEM) { mem->mem_type = mem_type; mem->placement = cur_flags; mem->mm_node = NULL; return 0; } ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem, interruptible, no_wait_gpu); if (ret == 0 && mem->mm_node) { mem->placement = cur_flags; return 0; } if (ret == -ERESTART) has_erestartsys = true; } ret = (has_erestartsys) ? -ERESTART : -ENOMEM; return ret; } static int ttm_bo_move_buffer(struct ttm_buffer_object *bo, struct ttm_placement *placement, bool interruptible, bool no_wait_gpu) { int ret = 0; struct ttm_mem_reg mem; struct ttm_bo_device *bdev = bo->bdev; MPASS(ttm_bo_is_reserved(bo)); /* * FIXME: It's possible to pipeline buffer moves. * Have the driver move function wait for idle when necessary, * instead of doing it here. */ mtx_lock(&bdev->fence_lock); ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu); mtx_unlock(&bdev->fence_lock); if (ret) return ret; mem.num_pages = bo->num_pages; mem.size = mem.num_pages << PAGE_SHIFT; mem.page_alignment = bo->mem.page_alignment; mem.bus.io_reserved_vm = false; mem.bus.io_reserved_count = 0; /* * Determine where to move the buffer. */ ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait_gpu); if (ret) goto out_unlock; ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait_gpu); out_unlock: if (ret && mem.mm_node) ttm_bo_mem_put(bo, &mem); return ret; } static int ttm_bo_mem_compat(struct ttm_placement *placement, struct ttm_mem_reg *mem) { int i; if (mem->mm_node && placement->lpfn != 0 && (mem->start < placement->fpfn || mem->start + mem->num_pages > placement->lpfn)) return -1; for (i = 0; i < placement->num_placement; i++) { if ((placement->placement[i] & mem->placement & TTM_PL_MASK_CACHING) && (placement->placement[i] & mem->placement & TTM_PL_MASK_MEM)) return i; } return -1; } int ttm_bo_validate(struct ttm_buffer_object *bo, struct ttm_placement *placement, bool interruptible, bool no_wait_gpu) { int ret; MPASS(ttm_bo_is_reserved(bo)); /* Check that range is valid */ if (placement->lpfn || placement->fpfn) if (placement->fpfn > placement->lpfn || (placement->lpfn - placement->fpfn) < bo->num_pages) return -EINVAL; /* * Check whether we need to move buffer. */ ret = ttm_bo_mem_compat(placement, &bo->mem); if (ret < 0) { ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait_gpu); if (ret) return ret; } else { /* * Use the access and other non-mapping-related flag bits from * the compatible memory placement flags to the active flags */ ttm_flag_masked(&bo->mem.placement, placement->placement[ret], ~TTM_PL_MASK_MEMTYPE); } /* * We might need to add a TTM. */ if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { ret = ttm_bo_add_ttm(bo, true); if (ret) return ret; } return 0; } int ttm_bo_check_placement(struct ttm_buffer_object *bo, struct ttm_placement *placement) { MPASS(!((placement->fpfn || placement->lpfn) && (bo->mem.num_pages > (placement->lpfn - placement->fpfn)))); return 0; } int ttm_bo_init(struct ttm_bo_device *bdev, struct ttm_buffer_object *bo, unsigned long size, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, bool interruptible, struct vm_object *persistent_swap_storage, size_t acc_size, struct sg_table *sg, void (*destroy) (struct ttm_buffer_object *)) { int ret = 0; unsigned long num_pages; struct ttm_mem_global *mem_glob = bdev->glob->mem_glob; ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false); if (ret) { printf("[TTM] Out of kernel memory\n"); if (destroy) (*destroy)(bo); else free(bo, M_TTM_BO); return -ENOMEM; } num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; if (num_pages == 0) { printf("[TTM] Illegal buffer object size\n"); if (destroy) (*destroy)(bo); else free(bo, M_TTM_BO); ttm_mem_global_free(mem_glob, acc_size); return -EINVAL; } bo->destroy = destroy; refcount_init(&bo->kref, 1); refcount_init(&bo->list_kref, 1); atomic_set(&bo->cpu_writers, 0); atomic_set(&bo->reserved, 1); INIT_LIST_HEAD(&bo->lru); INIT_LIST_HEAD(&bo->ddestroy); INIT_LIST_HEAD(&bo->swap); INIT_LIST_HEAD(&bo->io_reserve_lru); bo->bdev = bdev; bo->glob = bdev->glob; bo->type = type; bo->num_pages = num_pages; bo->mem.size = num_pages << PAGE_SHIFT; bo->mem.mem_type = TTM_PL_SYSTEM; bo->mem.num_pages = bo->num_pages; bo->mem.mm_node = NULL; bo->mem.page_alignment = page_alignment; bo->mem.bus.io_reserved_vm = false; bo->mem.bus.io_reserved_count = 0; bo->priv_flags = 0; bo->mem.placement = (TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED); bo->seq_valid = false; bo->persistent_swap_storage = persistent_swap_storage; bo->acc_size = acc_size; bo->sg = sg; atomic_inc(&bo->glob->bo_count); ret = ttm_bo_check_placement(bo, placement); if (unlikely(ret != 0)) goto out_err; /* * For ttm_bo_type_device buffers, allocate * address space from the device. */ if (bo->type == ttm_bo_type_device || bo->type == ttm_bo_type_sg) { ret = ttm_bo_setup_vm(bo); if (ret) goto out_err; } ret = ttm_bo_validate(bo, placement, interruptible, false); if (ret) goto out_err; ttm_bo_unreserve(bo); return 0; out_err: ttm_bo_unreserve(bo); ttm_bo_unref(&bo); return ret; } size_t ttm_bo_acc_size(struct ttm_bo_device *bdev, unsigned long bo_size, unsigned struct_size) { unsigned npages = (PAGE_ALIGN(bo_size)) >> PAGE_SHIFT; size_t size = 0; size += ttm_round_pot(struct_size); size += PAGE_ALIGN(npages * sizeof(void *)); size += ttm_round_pot(sizeof(struct ttm_tt)); return size; } size_t ttm_bo_dma_acc_size(struct ttm_bo_device *bdev, unsigned long bo_size, unsigned struct_size) { unsigned npages = (PAGE_ALIGN(bo_size)) >> PAGE_SHIFT; size_t size = 0; size += ttm_round_pot(struct_size); size += PAGE_ALIGN(npages * sizeof(void *)); size += PAGE_ALIGN(npages * sizeof(dma_addr_t)); size += ttm_round_pot(sizeof(struct ttm_dma_tt)); return size; } int ttm_bo_create(struct ttm_bo_device *bdev, unsigned long size, enum ttm_bo_type type, struct ttm_placement *placement, uint32_t page_alignment, bool interruptible, struct vm_object *persistent_swap_storage, struct ttm_buffer_object **p_bo) { struct ttm_buffer_object *bo; size_t acc_size; int ret; bo = malloc(sizeof(*bo), M_TTM_BO, M_WAITOK | M_ZERO); acc_size = ttm_bo_acc_size(bdev, size, sizeof(struct ttm_buffer_object)); ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment, interruptible, persistent_swap_storage, acc_size, NULL, NULL); if (likely(ret == 0)) *p_bo = bo; return ret; } static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, unsigned mem_type, bool allow_errors) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; struct ttm_bo_global *glob = bdev->glob; int ret; /* * Can't use standard list traversal since we're unlocking. */ mtx_lock(&glob->lru_lock); while (!list_empty(&man->lru)) { mtx_unlock(&glob->lru_lock); ret = ttm_mem_evict_first(bdev, mem_type, false, false); if (ret) { if (allow_errors) { return ret; } else { printf("[TTM] Cleanup eviction failed\n"); } } mtx_lock(&glob->lru_lock); } mtx_unlock(&glob->lru_lock); return 0; } int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type) { struct ttm_mem_type_manager *man; int ret = -EINVAL; if (mem_type >= TTM_NUM_MEM_TYPES) { printf("[TTM] Illegal memory type %d\n", mem_type); return ret; } man = &bdev->man[mem_type]; if (!man->has_type) { printf("[TTM] Trying to take down uninitialized memory manager type %u\n", mem_type); return ret; } man->use_type = false; man->has_type = false; ret = 0; if (mem_type > 0) { ttm_bo_force_list_clean(bdev, mem_type, false); ret = (*man->func->takedown)(man); } return ret; } int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; if (mem_type == 0 || mem_type >= TTM_NUM_MEM_TYPES) { printf("[TTM] Illegal memory manager memory type %u\n", mem_type); return -EINVAL; } if (!man->has_type) { printf("[TTM] Memory type %u has not been initialized\n", mem_type); return 0; } return ttm_bo_force_list_clean(bdev, mem_type, true); } int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, unsigned long p_size) { int ret = -EINVAL; struct ttm_mem_type_manager *man; MPASS(type < TTM_NUM_MEM_TYPES); man = &bdev->man[type]; MPASS(!man->has_type); man->io_reserve_fastpath = true; man->use_io_reserve_lru = false; sx_init(&man->io_reserve_mutex, "ttmman"); INIT_LIST_HEAD(&man->io_reserve_lru); ret = bdev->driver->init_mem_type(bdev, type, man); if (ret) return ret; man->bdev = bdev; ret = 0; if (type != TTM_PL_SYSTEM) { ret = (*man->func->init)(man, p_size); if (ret) return ret; } man->has_type = true; man->use_type = true; man->size = p_size; INIT_LIST_HEAD(&man->lru); return 0; } static void ttm_bo_global_kobj_release(struct ttm_bo_global *glob) { ttm_mem_unregister_shrink(glob->mem_glob, &glob->shrink); vm_page_free(glob->dummy_read_page); } void ttm_bo_global_release(struct drm_global_reference *ref) { struct ttm_bo_global *glob = ref->object; if (refcount_release(&glob->kobj_ref)) ttm_bo_global_kobj_release(glob); } int ttm_bo_global_init(struct drm_global_reference *ref) { struct ttm_bo_global_ref *bo_ref = container_of(ref, struct ttm_bo_global_ref, ref); struct ttm_bo_global *glob = ref->object; int ret; + int tries; sx_init(&glob->device_list_mutex, "ttmdlm"); mtx_init(&glob->lru_lock, "ttmlru", NULL, MTX_DEF); glob->mem_glob = bo_ref->mem_glob; + tries = 0; +retry: glob->dummy_read_page = vm_page_alloc_contig(NULL, 0, VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ, 1, 0, VM_MAX_ADDRESS, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); if (unlikely(glob->dummy_read_page == NULL)) { + if (tries < 1) { + vm_pageout_grow_cache(tries, 0, VM_MAX_ADDRESS); + tries++; + goto retry; + } ret = -ENOMEM; goto out_no_drp; } INIT_LIST_HEAD(&glob->swap_lru); INIT_LIST_HEAD(&glob->device_list); ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout); ret = ttm_mem_register_shrink(glob->mem_glob, &glob->shrink); if (unlikely(ret != 0)) { printf("[TTM] Could not register buffer object swapout\n"); goto out_no_shrink; } atomic_set(&glob->bo_count, 0); refcount_init(&glob->kobj_ref, 1); return (0); out_no_shrink: vm_page_free(glob->dummy_read_page); out_no_drp: free(glob, M_DRM_GLOBAL); return ret; } int ttm_bo_device_release(struct ttm_bo_device *bdev) { int ret = 0; unsigned i = TTM_NUM_MEM_TYPES; struct ttm_mem_type_manager *man; struct ttm_bo_global *glob = bdev->glob; while (i--) { man = &bdev->man[i]; if (man->has_type) { man->use_type = false; if ((i != TTM_PL_SYSTEM) && ttm_bo_clean_mm(bdev, i)) { ret = -EBUSY; printf("[TTM] DRM memory manager type %d is not clean\n", i); } man->has_type = false; } } sx_xlock(&glob->device_list_mutex); list_del(&bdev->device_list); sx_xunlock(&glob->device_list_mutex); if (taskqueue_cancel_timeout(taskqueue_thread, &bdev->wq, NULL)) taskqueue_drain_timeout(taskqueue_thread, &bdev->wq); while (ttm_bo_delayed_delete(bdev, true)) ; mtx_lock(&glob->lru_lock); if (list_empty(&bdev->ddestroy)) TTM_DEBUG("Delayed destroy list was clean\n"); if (list_empty(&bdev->man[0].lru)) TTM_DEBUG("Swap list was clean\n"); mtx_unlock(&glob->lru_lock); MPASS(drm_mm_clean(&bdev->addr_space_mm)); rw_wlock(&bdev->vm_lock); drm_mm_takedown(&bdev->addr_space_mm); rw_wunlock(&bdev->vm_lock); return ret; } int ttm_bo_device_init(struct ttm_bo_device *bdev, struct ttm_bo_global *glob, struct ttm_bo_driver *driver, uint64_t file_page_offset, bool need_dma32) { int ret = -EINVAL; rw_init(&bdev->vm_lock, "ttmvml"); bdev->driver = driver; memset(bdev->man, 0, sizeof(bdev->man)); /* * Initialize the system memory buffer type. * Other types need to be driver / IOCTL initialized. */ ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0); if (unlikely(ret != 0)) goto out_no_sys; RB_INIT(&bdev->addr_space_rb); ret = drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000); if (unlikely(ret != 0)) goto out_no_addr_mm; TIMEOUT_TASK_INIT(taskqueue_thread, &bdev->wq, 0, ttm_bo_delayed_workqueue, bdev); INIT_LIST_HEAD(&bdev->ddestroy); bdev->dev_mapping = NULL; bdev->glob = glob; bdev->need_dma32 = need_dma32; bdev->val_seq = 0; mtx_init(&bdev->fence_lock, "ttmfence", NULL, MTX_DEF); sx_xlock(&glob->device_list_mutex); list_add_tail(&bdev->device_list, &glob->device_list); sx_xunlock(&glob->device_list_mutex); return 0; out_no_addr_mm: ttm_bo_clean_mm(bdev, 0); out_no_sys: return ret; } /* * buffer object vm functions. */ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; if (!(man->flags & TTM_MEMTYPE_FLAG_FIXED)) { if (mem->mem_type == TTM_PL_SYSTEM) return false; if (man->flags & TTM_MEMTYPE_FLAG_CMA) return false; if (mem->placement & TTM_PL_FLAG_CACHED) return false; } return true; } void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { ttm_bo_release_mmap(bo); ttm_mem_io_free_vm(bo); } void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; ttm_mem_io_lock(man, false); ttm_bo_unmap_virtual_locked(bo); ttm_mem_io_unlock(man); } static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; /* The caller acquired bdev->vm_lock. */ RB_INSERT(ttm_bo_device_buffer_objects, &bdev->addr_space_rb, bo); } /** * ttm_bo_setup_vm: * * @bo: the buffer to allocate address space for * * Allocate address space in the drm device so that applications * can mmap the buffer and access the contents. This only * applies to ttm_bo_type_device objects as others are not * placed in the drm device address space. */ static int ttm_bo_setup_vm(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; int ret; retry_pre_get: ret = drm_mm_pre_get(&bdev->addr_space_mm); if (unlikely(ret != 0)) return ret; rw_wlock(&bdev->vm_lock); bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm, bo->mem.num_pages, 0, 0); if (unlikely(bo->vm_node == NULL)) { ret = -ENOMEM; goto out_unlock; } bo->vm_node = drm_mm_get_block_atomic(bo->vm_node, bo->mem.num_pages, 0); if (unlikely(bo->vm_node == NULL)) { rw_wunlock(&bdev->vm_lock); goto retry_pre_get; } ttm_bo_vm_insert_rb(bo); rw_wunlock(&bdev->vm_lock); bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT; return 0; out_unlock: rw_wunlock(&bdev->vm_lock); return ret; } int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy, bool interruptible, bool no_wait) { struct ttm_bo_driver *driver = bo->bdev->driver; struct ttm_bo_device *bdev = bo->bdev; void *sync_obj; int ret = 0; if (likely(bo->sync_obj == NULL)) return 0; while (bo->sync_obj) { if (driver->sync_obj_signaled(bo->sync_obj)) { void *tmp_obj = bo->sync_obj; bo->sync_obj = NULL; clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); mtx_unlock(&bdev->fence_lock); driver->sync_obj_unref(&tmp_obj); mtx_lock(&bdev->fence_lock); continue; } if (no_wait) return -EBUSY; sync_obj = driver->sync_obj_ref(bo->sync_obj); mtx_unlock(&bdev->fence_lock); ret = driver->sync_obj_wait(sync_obj, lazy, interruptible); if (unlikely(ret != 0)) { driver->sync_obj_unref(&sync_obj); mtx_lock(&bdev->fence_lock); return ret; } mtx_lock(&bdev->fence_lock); if (likely(bo->sync_obj == sync_obj)) { void *tmp_obj = bo->sync_obj; bo->sync_obj = NULL; clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); mtx_unlock(&bdev->fence_lock); driver->sync_obj_unref(&sync_obj); driver->sync_obj_unref(&tmp_obj); mtx_lock(&bdev->fence_lock); } else { mtx_unlock(&bdev->fence_lock); driver->sync_obj_unref(&sync_obj); mtx_lock(&bdev->fence_lock); } } return 0; } int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait) { struct ttm_bo_device *bdev = bo->bdev; int ret = 0; /* * Using ttm_bo_reserve makes sure the lru lists are updated. */ ret = ttm_bo_reserve(bo, true, no_wait, false, 0); if (unlikely(ret != 0)) return ret; mtx_lock(&bdev->fence_lock); ret = ttm_bo_wait(bo, false, true, no_wait); mtx_unlock(&bdev->fence_lock); if (likely(ret == 0)) atomic_inc(&bo->cpu_writers); ttm_bo_unreserve(bo); return ret; } void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo) { atomic_dec(&bo->cpu_writers); } /** * A buffer object shrink method that tries to swap out the first * buffer object on the bo_global::swap_lru list. */ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) { struct ttm_bo_global *glob = container_of(shrink, struct ttm_bo_global, shrink); struct ttm_buffer_object *bo; int ret = -EBUSY; int put_count; uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM); mtx_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } if (ret) { mtx_unlock(&glob->lru_lock); return ret; } refcount_acquire(&bo->list_kref); if (!list_empty(&bo->ddestroy)) { ret = ttm_bo_cleanup_refs_and_unlock(bo, false, false); if (refcount_release(&bo->list_kref)) ttm_bo_release_list(bo); return ret; } put_count = ttm_bo_del_from_lru(bo); mtx_unlock(&glob->lru_lock); ttm_bo_list_ref_sub(bo, put_count, true); /** * Wait for GPU, then move to system cached. */ mtx_lock(&bo->bdev->fence_lock); ret = ttm_bo_wait(bo, false, false, false); mtx_unlock(&bo->bdev->fence_lock); if (unlikely(ret != 0)) goto out; if ((bo->mem.placement & swap_placement) != swap_placement) { struct ttm_mem_reg evict_mem; evict_mem = bo->mem; evict_mem.mm_node = NULL; evict_mem.placement = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED; evict_mem.mem_type = TTM_PL_SYSTEM; ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, false, false); if (unlikely(ret != 0)) goto out; } ttm_bo_unmap_virtual(bo); /** * Swap out. Buffer will be swapped in again as soon as * anyone tries to access a ttm page. */ if (bo->bdev->driver->swap_notify) bo->bdev->driver->swap_notify(bo); ret = ttm_tt_swapout(bo->ttm, bo->persistent_swap_storage); out: /** * * Unreserve without putting on LRU to avoid swapping out an * already swapped buffer. */ atomic_set(&bo->reserved, 0); wakeup(bo); if (refcount_release(&bo->list_kref)) ttm_bo_release_list(bo); return ret; } void ttm_bo_swapout_all(struct ttm_bo_device *bdev) { while (ttm_bo_swapout(&bdev->glob->shrink) == 0) ; } Index: projects/building-blocks/sys/dev/drm2/ttm/ttm_page_alloc.c =================================================================== --- projects/building-blocks/sys/dev/drm2/ttm/ttm_page_alloc.c (revision 278195) +++ projects/building-blocks/sys/dev/drm2/ttm/ttm_page_alloc.c (revision 278196) @@ -1,885 +1,910 @@ /* * Copyright (c) Red Hat Inc. * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * 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 (including the * next paragraph) 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 NON-INFRINGEMENT. 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. * * Authors: Dave Airlie * Jerome Glisse * Pauli Nieminen */ /* * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. */ /* simple list based uncached page pool * - Pool collects resently freed pages for reuse * - Use page->lru to keep a free list * - doesn't track currently in use pages */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(vm_page_t)) #define SMALL_ALLOCATION 16 #define FREE_ALL_PAGES (~0U) /* times are in msecs */ #define PAGE_FREE_INTERVAL 1000 /** * struct ttm_page_pool - Pool to reuse recently allocated uc/wc pages. * * @lock: Protects the shared pool from concurrnet access. Must be used with * irqsave/irqrestore variants because pool allocator maybe called from * delayed work. * @fill_lock: Prevent concurrent calls to fill. * @list: Pool of free uc/wc pages for fast reuse. * @gfp_flags: Flags to pass for alloc_page. * @npages: Number of pages in pool. */ struct ttm_page_pool { struct mtx lock; bool fill_lock; bool dma32; struct pglist list; int ttm_page_alloc_flags; unsigned npages; char *name; unsigned long nfrees; unsigned long nrefills; }; /** * Limits for the pool. They are handled without locks because only place where * they may change is in sysfs store. They won't have immediate effect anyway * so forcing serialization to access them is pointless. */ struct ttm_pool_opts { unsigned alloc_size; unsigned max_size; unsigned small; }; #define NUM_POOLS 4 /** * struct ttm_pool_manager - Holds memory pools for fst allocation * * Manager is read only object for pool code so it doesn't need locking. * * @free_interval: minimum number of jiffies between freeing pages from pool. * @page_alloc_inited: reference counting for pool allocation. * @work: Work that is used to shrink the pool. Work is only run when there is * some pages to free. * @small_allocation: Limit in number of pages what is small allocation. * * @pools: All pool objects in use. **/ struct ttm_pool_manager { unsigned int kobj_ref; eventhandler_tag lowmem_handler; struct ttm_pool_opts options; union { struct ttm_page_pool u_pools[NUM_POOLS]; struct _utag { struct ttm_page_pool u_wc_pool; struct ttm_page_pool u_uc_pool; struct ttm_page_pool u_wc_pool_dma32; struct ttm_page_pool u_uc_pool_dma32; } _ut; } _u; }; #define pools _u.u_pools #define wc_pool _u._ut.u_wc_pool #define uc_pool _u._ut.u_uc_pool #define wc_pool_dma32 _u._ut.u_wc_pool_dma32 #define uc_pool_dma32 _u._ut.u_uc_pool_dma32 MALLOC_DEFINE(M_TTM_POOLMGR, "ttm_poolmgr", "TTM Pool Manager"); static void ttm_vm_page_free(vm_page_t m) { KASSERT(m->object == NULL, ("ttm page %p is owned", m)); KASSERT(m->wire_count == 1, ("ttm lost wire %p", m)); KASSERT((m->flags & PG_FICTITIOUS) != 0, ("ttm lost fictitious %p", m)); KASSERT((m->oflags & VPO_UNMANAGED) == 0, ("ttm got unmanaged %p", m)); m->flags &= ~PG_FICTITIOUS; m->oflags |= VPO_UNMANAGED; vm_page_unwire(m, PQ_INACTIVE); vm_page_free(m); } static vm_memattr_t ttm_caching_state_to_vm(enum ttm_caching_state cstate) { switch (cstate) { case tt_uncached: return (VM_MEMATTR_UNCACHEABLE); case tt_wc: return (VM_MEMATTR_WRITE_COMBINING); case tt_cached: return (VM_MEMATTR_WRITE_BACK); } panic("caching state %d\n", cstate); } static void ttm_pool_kobj_release(struct ttm_pool_manager *m) { free(m, M_TTM_POOLMGR); } #if 0 /* XXXKIB sysctl */ static ssize_t ttm_pool_store(struct ttm_pool_manager *m, struct attribute *attr, const char *buffer, size_t size) { int chars; unsigned val; chars = sscanf(buffer, "%u", &val); if (chars == 0) return size; /* Convert kb to number of pages */ val = val / (PAGE_SIZE >> 10); if (attr == &ttm_page_pool_max) m->options.max_size = val; else if (attr == &ttm_page_pool_small) m->options.small = val; else if (attr == &ttm_page_pool_alloc_size) { if (val > NUM_PAGES_TO_ALLOC*8) { pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n", NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7), NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); return size; } else if (val > NUM_PAGES_TO_ALLOC) { pr_warn("Setting allocation size to larger than %lu is not recommended\n", NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10)); } m->options.alloc_size = val; } return size; } static ssize_t ttm_pool_show(struct ttm_pool_manager *m, struct attribute *attr, char *buffer) { unsigned val = 0; if (attr == &ttm_page_pool_max) val = m->options.max_size; else if (attr == &ttm_page_pool_small) val = m->options.small; else if (attr == &ttm_page_pool_alloc_size) val = m->options.alloc_size; val = val * (PAGE_SIZE >> 10); return snprintf(buffer, PAGE_SIZE, "%u\n", val); } #endif static struct ttm_pool_manager *_manager; static int set_pages_array_wb(vm_page_t *pages, int addrinarray) { #ifdef TTM_HAS_AGP int i; for (i = 0; i < addrinarray; i++) pmap_page_set_memattr(pages[i], VM_MEMATTR_WRITE_BACK); #endif return 0; } static int set_pages_array_wc(vm_page_t *pages, int addrinarray) { #ifdef TTM_HAS_AGP int i; for (i = 0; i < addrinarray; i++) pmap_page_set_memattr(pages[i], VM_MEMATTR_WRITE_COMBINING); #endif return 0; } static int set_pages_array_uc(vm_page_t *pages, int addrinarray) { #ifdef TTM_HAS_AGP int i; for (i = 0; i < addrinarray; i++) pmap_page_set_memattr(pages[i], VM_MEMATTR_UNCACHEABLE); #endif return 0; } /** * Select the right pool or requested caching state and ttm flags. */ static struct ttm_page_pool *ttm_get_pool(int flags, enum ttm_caching_state cstate) { int pool_index; if (cstate == tt_cached) return NULL; if (cstate == tt_wc) pool_index = 0x0; else pool_index = 0x1; if (flags & TTM_PAGE_FLAG_DMA32) pool_index |= 0x2; return &_manager->pools[pool_index]; } /* set memory back to wb and free the pages. */ static void ttm_pages_put(vm_page_t *pages, unsigned npages) { unsigned i; /* Our VM handles vm memattr automatically on the page free. */ if (set_pages_array_wb(pages, npages)) printf("[TTM] Failed to set %d pages to wb!\n", npages); for (i = 0; i < npages; ++i) ttm_vm_page_free(pages[i]); } static void ttm_pool_update_free_locked(struct ttm_page_pool *pool, unsigned freed_pages) { pool->npages -= freed_pages; pool->nfrees += freed_pages; } /** * Free pages from pool. * * To prevent hogging the ttm_swap process we only free NUM_PAGES_TO_ALLOC * number of pages in one go. * * @pool: to free the pages from * @free_all: If set to true will free all pages in pool **/ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free) { vm_page_t p, p1; vm_page_t *pages_to_free; unsigned freed_pages = 0, npages_to_free = nr_free; unsigned i; if (NUM_PAGES_TO_ALLOC < nr_free) npages_to_free = NUM_PAGES_TO_ALLOC; pages_to_free = malloc(npages_to_free * sizeof(vm_page_t), M_TEMP, M_WAITOK | M_ZERO); restart: mtx_lock(&pool->lock); TAILQ_FOREACH_REVERSE_SAFE(p, &pool->list, pglist, plinks.q, p1) { if (freed_pages >= npages_to_free) break; pages_to_free[freed_pages++] = p; /* We can only remove NUM_PAGES_TO_ALLOC at a time. */ if (freed_pages >= NUM_PAGES_TO_ALLOC) { /* remove range of pages from the pool */ for (i = 0; i < freed_pages; i++) TAILQ_REMOVE(&pool->list, pages_to_free[i], plinks.q); ttm_pool_update_free_locked(pool, freed_pages); /** * Because changing page caching is costly * we unlock the pool to prevent stalling. */ mtx_unlock(&pool->lock); ttm_pages_put(pages_to_free, freed_pages); if (likely(nr_free != FREE_ALL_PAGES)) nr_free -= freed_pages; if (NUM_PAGES_TO_ALLOC >= nr_free) npages_to_free = nr_free; else npages_to_free = NUM_PAGES_TO_ALLOC; freed_pages = 0; /* free all so restart the processing */ if (nr_free) goto restart; /* Not allowed to fall through or break because * following context is inside spinlock while we are * outside here. */ goto out; } } /* remove range of pages from the pool */ if (freed_pages) { for (i = 0; i < freed_pages; i++) TAILQ_REMOVE(&pool->list, pages_to_free[i], plinks.q); ttm_pool_update_free_locked(pool, freed_pages); nr_free -= freed_pages; } mtx_unlock(&pool->lock); if (freed_pages) ttm_pages_put(pages_to_free, freed_pages); out: free(pages_to_free, M_TEMP); return nr_free; } /* Get good estimation how many pages are free in pools */ static int ttm_pool_get_num_unused_pages(void) { unsigned i; int total = 0; for (i = 0; i < NUM_POOLS; ++i) total += _manager->pools[i].npages; return total; } /** * Callback for mm to request pool to reduce number of page held. */ static int ttm_pool_mm_shrink(void *arg) { static unsigned int start_pool = 0; unsigned i; unsigned pool_offset = atomic_fetchadd_int(&start_pool, 1); struct ttm_page_pool *pool; int shrink_pages = 100; /* XXXKIB */ pool_offset = pool_offset % NUM_POOLS; /* select start pool in round robin fashion */ for (i = 0; i < NUM_POOLS; ++i) { unsigned nr_free = shrink_pages; if (shrink_pages == 0) break; pool = &_manager->pools[(i + pool_offset)%NUM_POOLS]; shrink_pages = ttm_page_pool_free(pool, nr_free); } /* return estimated number of unused pages in pool */ return ttm_pool_get_num_unused_pages(); } static void ttm_pool_mm_shrink_init(struct ttm_pool_manager *manager) { manager->lowmem_handler = EVENTHANDLER_REGISTER(vm_lowmem, ttm_pool_mm_shrink, manager, EVENTHANDLER_PRI_ANY); } static void ttm_pool_mm_shrink_fini(struct ttm_pool_manager *manager) { EVENTHANDLER_DEREGISTER(vm_lowmem, manager->lowmem_handler); } static int ttm_set_pages_caching(vm_page_t *pages, enum ttm_caching_state cstate, unsigned cpages) { int r = 0; /* Set page caching */ switch (cstate) { case tt_uncached: r = set_pages_array_uc(pages, cpages); if (r) printf("[TTM] Failed to set %d pages to uc!\n", cpages); break; case tt_wc: r = set_pages_array_wc(pages, cpages); if (r) printf("[TTM] Failed to set %d pages to wc!\n", cpages); break; default: break; } return r; } /** * Free pages the pages that failed to change the caching state. If there is * any pages that have changed their caching state already put them to the * pool. */ static void ttm_handle_caching_state_failure(struct pglist *pages, int ttm_flags, enum ttm_caching_state cstate, vm_page_t *failed_pages, unsigned cpages) { unsigned i; /* Failed pages have to be freed */ for (i = 0; i < cpages; ++i) { TAILQ_REMOVE(pages, failed_pages[i], plinks.q); ttm_vm_page_free(failed_pages[i]); } } +static vm_paddr_t +ttm_alloc_high_bound(int ttm_alloc_flags) +{ + + return ((ttm_alloc_flags & TTM_PAGE_FLAG_DMA32) ? 0xffffffff : + VM_MAX_ADDRESS); +} + /** * Allocate new pages with correct caching. * * This function is reentrant if caller updates count depending on number of * pages returned in pages array. */ static int ttm_alloc_new_pages(struct pglist *pages, int ttm_alloc_flags, int ttm_flags, enum ttm_caching_state cstate, unsigned count) { vm_page_t *caching_array; vm_page_t p; int r = 0; unsigned i, cpages, aflags; unsigned max_cpages = min(count, (unsigned)(PAGE_SIZE/sizeof(vm_page_t))); + int tries; aflags = VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_NOOBJ | ((ttm_alloc_flags & TTM_PAGE_FLAG_ZERO_ALLOC) != 0 ? VM_ALLOC_ZERO : 0); /* allocate array for page caching change */ caching_array = malloc(max_cpages * sizeof(vm_page_t), M_TEMP, M_WAITOK | M_ZERO); for (i = 0, cpages = 0; i < count; ++i) { + tries = 0; +retry: p = vm_page_alloc_contig(NULL, 0, aflags, 1, 0, - (ttm_alloc_flags & TTM_PAGE_FLAG_DMA32) ? 0xffffffff : - VM_MAX_ADDRESS, PAGE_SIZE, 0, - ttm_caching_state_to_vm(cstate)); + ttm_alloc_high_bound(ttm_alloc_flags), + PAGE_SIZE, 0, ttm_caching_state_to_vm(cstate)); if (!p) { + if (tries < 3) { + vm_pageout_grow_cache(tries, 0, + ttm_alloc_high_bound(ttm_alloc_flags)); + tries++; + goto retry; + } printf("[TTM] Unable to get page %u\n", i); /* store already allocated pages in the pool after * setting the caching state */ if (cpages) { r = ttm_set_pages_caching(caching_array, cstate, cpages); if (r) ttm_handle_caching_state_failure(pages, ttm_flags, cstate, caching_array, cpages); } r = -ENOMEM; goto out; } p->oflags &= ~VPO_UNMANAGED; p->flags |= PG_FICTITIOUS; #ifdef CONFIG_HIGHMEM /* KIB: nop */ /* gfp flags of highmem page should never be dma32 so we * we should be fine in such case */ if (!PageHighMem(p)) #endif { caching_array[cpages++] = p; if (cpages == max_cpages) { r = ttm_set_pages_caching(caching_array, cstate, cpages); if (r) { ttm_handle_caching_state_failure(pages, ttm_flags, cstate, caching_array, cpages); goto out; } cpages = 0; } } TAILQ_INSERT_HEAD(pages, p, plinks.q); } if (cpages) { r = ttm_set_pages_caching(caching_array, cstate, cpages); if (r) ttm_handle_caching_state_failure(pages, ttm_flags, cstate, caching_array, cpages); } out: free(caching_array, M_TEMP); return r; } /** * Fill the given pool if there aren't enough pages and the requested number of * pages is small. */ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool, int ttm_flags, enum ttm_caching_state cstate, unsigned count) { vm_page_t p; int r; unsigned cpages = 0; /** * Only allow one pool fill operation at a time. * If pool doesn't have enough pages for the allocation new pages are * allocated from outside of pool. */ if (pool->fill_lock) return; pool->fill_lock = true; /* If allocation request is small and there are not enough * pages in a pool we fill the pool up first. */ if (count < _manager->options.small && count > pool->npages) { struct pglist new_pages; unsigned alloc_size = _manager->options.alloc_size; /** * Can't change page caching if in irqsave context. We have to * drop the pool->lock. */ mtx_unlock(&pool->lock); TAILQ_INIT(&new_pages); r = ttm_alloc_new_pages(&new_pages, pool->ttm_page_alloc_flags, ttm_flags, cstate, alloc_size); mtx_lock(&pool->lock); if (!r) { TAILQ_CONCAT(&pool->list, &new_pages, plinks.q); ++pool->nrefills; pool->npages += alloc_size; } else { printf("[TTM] Failed to fill pool (%p)\n", pool); /* If we have any pages left put them to the pool. */ TAILQ_FOREACH(p, &pool->list, plinks.q) { ++cpages; } TAILQ_CONCAT(&pool->list, &new_pages, plinks.q); pool->npages += cpages; } } pool->fill_lock = false; } /** * Cut 'count' number of pages from the pool and put them on the return list. * * @return count of pages still required to fulfill the request. */ static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool, struct pglist *pages, int ttm_flags, enum ttm_caching_state cstate, unsigned count) { vm_page_t p; unsigned i; mtx_lock(&pool->lock); ttm_page_pool_fill_locked(pool, ttm_flags, cstate, count); if (count >= pool->npages) { /* take all pages from the pool */ TAILQ_CONCAT(pages, &pool->list, plinks.q); count -= pool->npages; pool->npages = 0; goto out; } for (i = 0; i < count; i++) { p = TAILQ_FIRST(&pool->list); TAILQ_REMOVE(&pool->list, p, plinks.q); TAILQ_INSERT_TAIL(pages, p, plinks.q); } pool->npages -= count; count = 0; out: mtx_unlock(&pool->lock); return count; } /* Put all pages in pages list to correct pool to wait for reuse */ static void ttm_put_pages(vm_page_t *pages, unsigned npages, int flags, enum ttm_caching_state cstate) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); unsigned i; if (pool == NULL) { /* No pool for this memory type so free the pages */ for (i = 0; i < npages; i++) { if (pages[i]) { ttm_vm_page_free(pages[i]); pages[i] = NULL; } } return; } mtx_lock(&pool->lock); for (i = 0; i < npages; i++) { if (pages[i]) { TAILQ_INSERT_TAIL(&pool->list, pages[i], plinks.q); pages[i] = NULL; pool->npages++; } } /* Check that we don't go over the pool limit */ npages = 0; if (pool->npages > _manager->options.max_size) { npages = pool->npages - _manager->options.max_size; /* free at least NUM_PAGES_TO_ALLOC number of pages * to reduce calls to set_memory_wb */ if (npages < NUM_PAGES_TO_ALLOC) npages = NUM_PAGES_TO_ALLOC; } mtx_unlock(&pool->lock); if (npages) ttm_page_pool_free(pool, npages); } /* * On success pages list will hold count number of correctly * cached pages. */ static int ttm_get_pages(vm_page_t *pages, unsigned npages, int flags, enum ttm_caching_state cstate) { struct ttm_page_pool *pool = ttm_get_pool(flags, cstate); struct pglist plist; vm_page_t p = NULL; int gfp_flags, aflags; unsigned count; int r; + int tries; aflags = VM_ALLOC_NORMAL | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | ((flags & TTM_PAGE_FLAG_ZERO_ALLOC) != 0 ? VM_ALLOC_ZERO : 0); /* No pool for cached pages */ if (pool == NULL) { for (r = 0; r < npages; ++r) { + tries = 0; +retry: p = vm_page_alloc_contig(NULL, 0, aflags, 1, 0, - (flags & TTM_PAGE_FLAG_DMA32) ? 0xffffffff : - VM_MAX_ADDRESS, PAGE_SIZE, + ttm_alloc_high_bound(flags), PAGE_SIZE, 0, ttm_caching_state_to_vm(cstate)); if (!p) { + if (tries < 3) { + vm_pageout_grow_cache(tries, 0, + ttm_alloc_high_bound(flags)); + tries++; + goto retry; + } printf("[TTM] Unable to allocate page\n"); return -ENOMEM; } p->oflags &= ~VPO_UNMANAGED; p->flags |= PG_FICTITIOUS; pages[r] = p; } return 0; } /* combine zero flag to pool flags */ gfp_flags = flags | pool->ttm_page_alloc_flags; /* First we take pages from the pool */ TAILQ_INIT(&plist); npages = ttm_page_pool_get_pages(pool, &plist, flags, cstate, npages); count = 0; TAILQ_FOREACH(p, &plist, plinks.q) { pages[count++] = p; } /* clear the pages coming from the pool if requested */ if (flags & TTM_PAGE_FLAG_ZERO_ALLOC) { TAILQ_FOREACH(p, &plist, plinks.q) { pmap_zero_page(p); } } /* If pool didn't have enough pages allocate new one. */ if (npages > 0) { /* ttm_alloc_new_pages doesn't reference pool so we can run * multiple requests in parallel. **/ TAILQ_INIT(&plist); r = ttm_alloc_new_pages(&plist, gfp_flags, flags, cstate, npages); TAILQ_FOREACH(p, &plist, plinks.q) { pages[count++] = p; } if (r) { /* If there is any pages in the list put them back to * the pool. */ printf("[TTM] Failed to allocate extra pages for large request\n"); ttm_put_pages(pages, count, flags, cstate); return r; } } return 0; } static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, int flags, char *name) { mtx_init(&pool->lock, "ttmpool", NULL, MTX_DEF); pool->fill_lock = false; TAILQ_INIT(&pool->list); pool->npages = pool->nfrees = 0; pool->ttm_page_alloc_flags = flags; pool->name = name; } int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) { if (_manager != NULL) printf("[TTM] manager != NULL\n"); printf("[TTM] Initializing pool allocator\n"); _manager = malloc(sizeof(*_manager), M_TTM_POOLMGR, M_WAITOK | M_ZERO); ttm_page_pool_init_locked(&_manager->wc_pool, 0, "wc"); ttm_page_pool_init_locked(&_manager->uc_pool, 0, "uc"); ttm_page_pool_init_locked(&_manager->wc_pool_dma32, TTM_PAGE_FLAG_DMA32, "wc dma"); ttm_page_pool_init_locked(&_manager->uc_pool_dma32, TTM_PAGE_FLAG_DMA32, "uc dma"); _manager->options.max_size = max_pages; _manager->options.small = SMALL_ALLOCATION; _manager->options.alloc_size = NUM_PAGES_TO_ALLOC; refcount_init(&_manager->kobj_ref, 1); ttm_pool_mm_shrink_init(_manager); return 0; } void ttm_page_alloc_fini(void) { int i; printf("[TTM] Finalizing pool allocator\n"); ttm_pool_mm_shrink_fini(_manager); for (i = 0; i < NUM_POOLS; ++i) ttm_page_pool_free(&_manager->pools[i], FREE_ALL_PAGES); if (refcount_release(&_manager->kobj_ref)) ttm_pool_kobj_release(_manager); _manager = NULL; } int ttm_pool_populate(struct ttm_tt *ttm) { struct ttm_mem_global *mem_glob = ttm->glob->mem_glob; unsigned i; int ret; if (ttm->state != tt_unpopulated) return 0; for (i = 0; i < ttm->num_pages; ++i) { ret = ttm_get_pages(&ttm->pages[i], 1, ttm->page_flags, ttm->caching_state); if (ret != 0) { ttm_pool_unpopulate(ttm); return -ENOMEM; } ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], false, false); if (unlikely(ret != 0)) { ttm_pool_unpopulate(ttm); return -ENOMEM; } } if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) { ret = ttm_tt_swapin(ttm); if (unlikely(ret != 0)) { ttm_pool_unpopulate(ttm); return ret; } } ttm->state = tt_unbound; return 0; } void ttm_pool_unpopulate(struct ttm_tt *ttm) { unsigned i; for (i = 0; i < ttm->num_pages; ++i) { if (ttm->pages[i]) { ttm_mem_global_free_page(ttm->glob->mem_glob, ttm->pages[i]); ttm_put_pages(&ttm->pages[i], 1, ttm->page_flags, ttm->caching_state); } } ttm->state = tt_unpopulated; } #if 0 /* XXXKIB sysctl */ int ttm_page_alloc_debugfs(struct seq_file *m, void *data) { struct ttm_page_pool *p; unsigned i; char *h[] = {"pool", "refills", "pages freed", "size"}; if (!_manager) { seq_printf(m, "No pool allocator running.\n"); return 0; } seq_printf(m, "%6s %12s %13s %8s\n", h[0], h[1], h[2], h[3]); for (i = 0; i < NUM_POOLS; ++i) { p = &_manager->pools[i]; seq_printf(m, "%6s %12ld %13ld %8d\n", p->name, p->nrefills, p->nfrees, p->npages); } return 0; } #endif Index: projects/building-blocks/sys/dev/fe/if_fe_isa.c =================================================================== --- projects/building-blocks/sys/dev/fe/if_fe_isa.c (revision 278195) +++ projects/building-blocks/sys/dev/fe/if_fe_isa.c (revision 278196) @@ -1,1053 +1,1064 @@ /*- * All Rights Reserved, Copyright (C) Fujitsu Limited 1995 * * This software may be used, modified, copied, distributed, and sold, in * both source and binary form provided that the above copyright, these * terms and the following disclaimer are retained. The name of the author * and/or the contributor 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 AND THE CONTRIBUTOR ``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 THE CONTRIBUTOR 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 /* * ISA specific code. */ static int fe_isa_probe(device_t); static int fe_isa_attach(device_t); static device_method_t fe_isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fe_isa_probe), DEVMETHOD(device_attach, fe_isa_attach), { 0, 0 } }; static driver_t fe_isa_driver = { "fe", fe_isa_methods, sizeof (struct fe_softc) }; DRIVER_MODULE(fe, isa, fe_isa_driver, fe_devclass, 0, 0); static int fe_probe_ssi(device_t); static int fe_probe_jli(device_t); static int fe_probe_fmv(device_t); static int fe_probe_lnx(device_t); static int fe_probe_gwy(device_t); static int fe_probe_ubn(device_t); /* * Determine if the device is present at a specified I/O address. The * main entry to the driver. */ static int fe_isa_probe(device_t dev) { struct fe_softc *sc; int error; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); /* Prepare for the softc struct. */ sc = device_get_softc(dev); sc->sc_unit = device_get_unit(dev); /* Probe for supported boards. */ if ((error = fe_probe_ssi(dev)) == 0) goto end; fe_release_resource(dev); if ((error = fe_probe_jli(dev)) == 0) goto end; fe_release_resource(dev); if ((error = fe_probe_fmv(dev)) == 0) goto end; fe_release_resource(dev); if ((error = fe_probe_lnx(dev)) == 0) goto end; fe_release_resource(dev); if ((error = fe_probe_ubn(dev)) == 0) goto end; fe_release_resource(dev); if ((error = fe_probe_gwy(dev)) == 0) goto end; fe_release_resource(dev); end: if (error == 0) error = fe_alloc_irq(dev, 0); fe_release_resource(dev); return (error); } static int fe_isa_attach(device_t dev) { struct fe_softc *sc = device_get_softc(dev); + int error = 0; - if (sc->port_used) - fe_alloc_port(dev, sc->port_used); - fe_alloc_irq(dev, 0); + /* + * Note: these routines aren't expected to fail since we also call + * them in the probe routine. But coverity complains, so we'll honor + * that complaint since the intention here was never to ignore them.. + */ + if (sc->port_used) { + error = fe_alloc_port(dev, sc->port_used); + if (error != 0) + return (error); + } + error = fe_alloc_irq(dev, 0); + if (error != 0) + return (error); return fe_attach(dev); } /* * Probe and initialization for Fujitsu FMV-180 series boards */ static void fe_init_fmv(struct fe_softc *sc) { /* Initialize ASIC. */ fe_outb(sc, FE_FMV3, 0); fe_outb(sc, FE_FMV10, 0); #if 0 /* "Refresh" hardware configuration. FIXME. */ fe_outb(sc, FE_FMV2, fe_inb(sc, FE_FMV2)); #endif /* Turn the "master interrupt control" flag of ASIC on. */ fe_outb(sc, FE_FMV3, FE_FMV3_IRQENB); } static void fe_msel_fmv184(struct fe_softc *sc) { u_char port; /* FMV-184 has a special "register" to switch between AUI/BNC. Determine the value to write into the register, based on the user-specified media selection. */ port = (IFM_SUBTYPE(sc->media.ifm_media) == IFM_10_2) ? 0x00 : 0x01; /* The register is #5 on exntesion register bank... (Details of the register layout is not yet discovered.) */ fe_outb(sc, 0x1B, 0x46); /* ??? */ fe_outb(sc, 0x1E, 0x04); /* select ex-reg #4. */ fe_outb(sc, 0x1F, 0xC8); /* ??? */ fe_outb(sc, 0x1E, 0x05); /* select ex-reg #5. */ fe_outb(sc, 0x1F, port); /* Switch the media. */ fe_outb(sc, 0x1E, 0x04); /* select ex-reg #4. */ fe_outb(sc, 0x1F, 0x00); /* ??? */ fe_outb(sc, 0x1B, 0x00); /* ??? */ /* Make sure to select "external tranceiver" on MB86964. */ fe_outb(sc, FE_BMPR13, sc->proto_bmpr13 | FE_B13_PORT_AUI); } static int fe_probe_fmv(device_t dev) { struct fe_softc *sc = device_get_softc(dev); int n; u_long iobase, irq; static u_short const irqmap [ 4 ] = { 3, 7, 10, 15 }; static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR2, 0x71, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { FE_FMV0, 0x78, 0x50 }, /* ERRDY+PRRDY */ { FE_FMV1, 0xB0, 0x00 }, /* FMV-183/4 has 0x48 bits. */ { FE_FMV3, 0x7F, 0x00 }, { 0 } }; /* Board subtypes; it lists known FMV-180 variants. */ struct subtype { u_short mcode; u_short mbitmap; u_short defmedia; char const * str; }; static struct subtype const typelist [] = { { 0x0005, MB_HA|MB_HT|MB_H5, MB_HA, "FMV-181" }, { 0x0105, MB_HA|MB_HT|MB_H5, MB_HA, "FMV-181A" }, { 0x0003, MB_HM, MB_HM, "FMV-182" }, { 0x0103, MB_HM, MB_HM, "FMV-182A" }, { 0x0804, MB_HT, MB_HT, "FMV-183" }, { 0x0C04, MB_HT, MB_HT, "FMV-183 (on-board)" }, { 0x0803, MB_H2|MB_H5, MB_H2, "FMV-184" }, { 0, MB_HA, MB_HA, "unknown FMV-180 (?)" }, }; struct subtype const * type; /* Media indicator and "Hardware revision ID" */ u_short mcode; /* See if the specified address is possible for FMV-180 series. 220, 240, 260, 280, 2A0, 2C0, 300, and 340 are allowed for all boards, and 200, 2E0, 320, 360, 380, 3A0, 3C0, and 3E0 for PnP boards. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; if ((iobase & ~0x1E0) != 0x200) return ENXIO; /* FMV-180 occupies 32 I/O addresses. */ if (fe_alloc_port(dev, 32)) return ENXIO; /* Setup an I/O address mapping table and some others. */ fe_softc_defaults(sc); /* Simple probe. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* Get our station address from EEPROM, and make sure it is Fujitsu's. */ fe_inblk(sc, FE_FMV4, sc->enaddr, ETHER_ADDR_LEN); if (!fe_valid_Ether_p(sc->enaddr, 0x00000E)) return ENXIO; /* Find the supported media and "hardware revision" to know the model identification. */ mcode = (fe_inb(sc, FE_FMV0) & FE_FMV0_MEDIA) | ((fe_inb(sc, FE_FMV1) & FE_FMV1_REV) << 8); /* Determine the card type. */ for (type = typelist; type->mcode != 0; type++) { if (type->mcode == mcode) break; } if (type->mcode == 0) { /* Unknown card type... Hope the driver works. */ sc->stability |= UNSTABLE_TYPE; if (bootverbose) { device_printf(dev, "unknown config: %x-%x-%x-%x\n", fe_inb(sc, FE_FMV0), fe_inb(sc, FE_FMV1), fe_inb(sc, FE_FMV2), fe_inb(sc, FE_FMV3)); } } /* Setup the board type and media information. */ sc->type = FE_TYPE_FMV; sc->typestr = type->str; sc->mbitmap = type->mbitmap; sc->defmedia = type->defmedia; sc->msel = fe_msel_965; if (type->mbitmap == (MB_H2 | MB_H5)) { /* FMV184 requires a special media selection procedure. */ sc->msel = fe_msel_fmv184; } /* * An FMV-180 has been probed. * Determine which IRQ to be used. * * In this version, we give a priority to the kernel config file. * If the EEPROM and config don't match, say it to the user for * an attention. */ n = (fe_inb(sc, FE_FMV2) & FE_FMV2_IRS) >> FE_FMV2_IRS_SHIFT; irq = 0; bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); if (irq == NO_IRQ) { /* Just use the probed value. */ bus_set_resource(dev, SYS_RES_IRQ, 0, irqmap[n], 1); } else if (irq != irqmap[n]) { /* Don't match. */ sc->stability |= UNSTABLE_IRQ; } /* We need an init hook to initialize ASIC before we start. */ sc->init = fe_init_fmv; return 0; } /* * Fujitsu MB86965 JLI mode probe routines. * * 86965 has a special operating mode called JLI (mode 0), under which * the chip interfaces with ISA bus with a software-programmable * configuration. (The Fujitsu document calls the feature "Plug and * play," but it is not compatible with the ISA-PnP spec. designed by * Intel and Microsoft.) Ethernet cards designed to use JLI are * almost same, but there are two things which require board-specific * probe routines: EEPROM layout and IRQ pin connection. * * JLI provides a handy way to access EEPROM which should contains the * chip configuration information (such as I/O port address) as well * as Ethernet station (MAC) address. The chip configuration info. is * stored on a fixed location. However, the station address can be * located anywhere in the EEPROM; it is up to the board designer to * determine the location. (The manual just says "somewhere in the * EEPROM.") The fe driver must somehow find out the correct * location. * * Another problem resides in the IRQ pin connection. JLI provides a * user to choose an IRQ from up to four predefined IRQs. The 86965 * chip has a register to select one out of the four possibilities. * However, the selection is against the four IRQ pins on the chip. * (So-called IRQ-A, -B, -C and -D.) It is (again) up to the board * designer to determine which pin to connect which IRQ line on the * ISA bus. We need a vendor (or model, for some vendor) specific IRQ * mapping table. * * The routine fe_probe_jli() provides all probe and initialization * processes which are common to all JLI implementation, and sub-probe * routines supply board-specific actions. * * JLI sub-probe routine has the following template: * * u_short const * func (struct fe_softc * sc, u_char const * eeprom); * * where eeprom is a pointer to an array of 32 byte data read from the * config EEPROM on the board. It retuns an IRQ mapping table for the * board, when the corresponding implementation is detected. It * returns a NULL otherwise. * * Primary purpose of the functin is to analize the config EEPROM, * determine if it matches with the pattern of that of supported card, * and extract necessary information from it. One of the information * expected to be extracted from EEPROM is the Ethernet station (MAC) * address, which must be set to the softc table of the interface by * the board-specific routine. */ /* JLI sub-probe for Allied-Telesyn/Allied-Telesis AT1700/RE2000 series. */ static u_short const * fe_probe_jli_ati(struct fe_softc * sc, u_char const * eeprom) { int i; static u_short const irqmaps_ati [4][4] = { { 3, 4, 5, 9 }, { 10, 11, 12, 15 }, { 3, 11, 5, 15 }, { 10, 11, 14, 15 }, }; /* Make sure the EEPROM contains Allied-Telesis/Allied-Telesyn bit pattern. */ if (eeprom[1] != 0x00) return NULL; for (i = 2; i < 8; i++) if (eeprom[i] != 0xFF) return NULL; for (i = 14; i < 24; i++) if (eeprom[i] != 0xFF) return NULL; /* Get our station address from EEPROM, and make sure the EEPROM contains ATI's address. */ bcopy(eeprom + 8, sc->enaddr, ETHER_ADDR_LEN); if (!fe_valid_Ether_p(sc->enaddr, 0x0000F4)) return NULL; /* * The following model identification codes are stolen * from the NetBSD port of the fe driver. My reviewers * suggested minor revision. */ /* Determine the card type. */ switch (eeprom[FE_ATI_EEP_MODEL]) { case FE_ATI_MODEL_AT1700T: sc->typestr = "AT-1700T/RE2001"; sc->mbitmap = MB_HT; sc->defmedia = MB_HT; break; case FE_ATI_MODEL_AT1700BT: sc->typestr = "AT-1700BT/RE2003"; sc->mbitmap = MB_HA | MB_HT | MB_H2; break; case FE_ATI_MODEL_AT1700FT: sc->typestr = "AT-1700FT/RE2009"; sc->mbitmap = MB_HA | MB_HT | MB_HF; break; case FE_ATI_MODEL_AT1700AT: sc->typestr = "AT-1700AT/RE2005"; sc->mbitmap = MB_HA | MB_HT | MB_H5; break; default: sc->typestr = "unknown AT-1700/RE2000"; sc->stability |= UNSTABLE_TYPE | UNSTABLE_IRQ; break; } sc->type = FE_TYPE_JLI; #if 0 /* Should we extract default media from eeprom? Linux driver for AT1700 does it, although previous releases of FreeBSD don't. FIXME. */ /* Determine the default media selection from the config EEPROM. The byte at offset EEP_MEDIA is believed to contain BMPR13 value to be set. We just ignore STP bit or squelch bit, since we don't support those. (It is intentional.) */ switch (eeprom[FE_ATI_EEP_MEDIA] & FE_B13_PORT) { case FE_B13_AUTO: sc->defmedia = MB_HA; break; case FE_B13_TP: sc->defmedia = MB_HT; break; case FE_B13_AUI: sc->defmedia = sc->mbitmap & (MB_H2|MB_H5|MB_H5); /*XXX*/ break; default: sc->defmedia = MB_HA; break; } /* Make sure the default media is compatible with the supported ones. */ if ((sc->defmedia & sc->mbitmap) == 0) { if (sc->defmedia == MB_HA) { sc->defmedia = MB_HT; } else { sc->defmedia = MB_HA; } } #endif /* * Try to determine IRQ settings. * Different models use different ranges of IRQs. */ switch ((eeprom[FE_ATI_EEP_REVISION] & 0xf0) |(eeprom[FE_ATI_EEP_MAGIC] & 0x04)) { case 0x30: case 0x34: return irqmaps_ati[3]; case 0x10: case 0x14: case 0x50: case 0x54: return irqmaps_ati[2]; case 0x44: case 0x64: return irqmaps_ati[1]; default: return irqmaps_ati[0]; } } /* JLI sub-probe and msel hook for ICL Ethernet. */ static void fe_msel_icl(struct fe_softc *sc) { u_char d4; /* Switch between UTP and "external tranceiver" as always. */ fe_msel_965(sc); /* The board needs one more bit (on DLCR4) be set appropriately. */ if (IFM_SUBTYPE(sc->media.ifm_media) == IFM_10_5) { d4 = sc->proto_dlcr4 | FE_D4_CNTRL; } else { d4 = sc->proto_dlcr4 & ~FE_D4_CNTRL; } fe_outb(sc, FE_DLCR4, d4); } static u_short const * fe_probe_jli_icl(struct fe_softc * sc, u_char const * eeprom) { int i; u_short defmedia; u_char d6; static u_short const irqmap_icl [4] = { 9, 10, 5, 15 }; /* Make sure the EEPROM contains ICL bit pattern. */ for (i = 24; i < 39; i++) { if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30) return NULL; } for (i = 112; i < 122; i++) { if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30) return NULL; } /* Make sure the EEPROM contains ICL's permanent station address. If it isn't, probably this board is not an ICL's. */ if (!fe_valid_Ether_p(eeprom+122, 0x00004B)) return NULL; /* Check if the "configured" Ethernet address in the EEPROM is valid. Use it if it is, or use the "permanent" address instead. */ if (fe_valid_Ether_p(eeprom+4, 0x020000)) { /* The configured address is valid. Use it. */ bcopy(eeprom+4, sc->enaddr, ETHER_ADDR_LEN); } else { /* The configured address is invalid. Use permanent. */ bcopy(eeprom+122, sc->enaddr, ETHER_ADDR_LEN); } /* Determine model and supported media. */ switch (eeprom[0x5E]) { case 0: sc->typestr = "EtherTeam16i/COMBO"; sc->mbitmap = MB_HA | MB_HT | MB_H5 | MB_H2; break; case 1: sc->typestr = "EtherTeam16i/TP"; sc->mbitmap = MB_HT; break; case 2: sc->typestr = "EtherTeam16i/ErgoPro"; sc->mbitmap = MB_HA | MB_HT | MB_H5; break; case 4: sc->typestr = "EtherTeam16i/DUO"; sc->mbitmap = MB_HA | MB_HT | MB_H2; break; default: sc->typestr = "EtherTeam16i"; sc->stability |= UNSTABLE_TYPE; if (bootverbose) { printf("fe%d: unknown model code %02x for EtherTeam16i\n", sc->sc_unit, eeprom[0x5E]); } break; } sc->type = FE_TYPE_JLI; /* I'm not sure the following msel hook is required by all models or COMBO only... FIXME. */ sc->msel = fe_msel_icl; /* Make the configured media selection the default media. */ switch (eeprom[0x28]) { case 0: defmedia = MB_HA; break; case 1: defmedia = MB_H5; break; case 2: defmedia = MB_HT; break; case 3: defmedia = MB_H2; break; default: if (bootverbose) { printf("fe%d: unknown default media: %02x\n", sc->sc_unit, eeprom[0x28]); } defmedia = MB_HA; break; } /* Make sure the default media is compatible with the supported media. */ if ((defmedia & sc->mbitmap) == 0) { if (bootverbose) { printf("fe%d: default media adjusted\n", sc->sc_unit); } defmedia = sc->mbitmap; } /* Keep the determined default media. */ sc->defmedia = defmedia; /* ICL has "fat" models. We have to program 86965 to properly reflect the hardware. */ d6 = sc->proto_dlcr6 & ~(FE_D6_BUFSIZ | FE_D6_BBW); switch ((eeprom[0x61] << 8) | eeprom[0x60]) { case 0x2008: d6 |= FE_D6_BUFSIZ_32KB | FE_D6_BBW_BYTE; break; case 0x4010: d6 |= FE_D6_BUFSIZ_64KB | FE_D6_BBW_WORD; break; default: /* We can't support it, since we don't know which bits to set in DLCR6. */ printf("fe%d: unknown SRAM config for ICL\n", sc->sc_unit); return NULL; } sc->proto_dlcr6 = d6; /* Returns the IRQ table for the ICL board. */ return irqmap_icl; } /* JLI sub-probe for RATOC REX-5586/5587. */ static u_short const * fe_probe_jli_rex(struct fe_softc * sc, u_char const * eeprom) { int i; static u_short const irqmap_rex [4] = { 3, 4, 5, NO_IRQ }; /* Make sure the EEPROM contains RATOC's config pattern. */ if (eeprom[1] != eeprom[0]) return NULL; for (i = 8; i < 32; i++) if (eeprom[i] != 0xFF) return NULL; /* Get our station address from EEPROM. Note that RATOC stores it "byte-swapped" in each word. (I don't know why.) So, we just can't use bcopy().*/ sc->enaddr[0] = eeprom[3]; sc->enaddr[1] = eeprom[2]; sc->enaddr[2] = eeprom[5]; sc->enaddr[3] = eeprom[4]; sc->enaddr[4] = eeprom[7]; sc->enaddr[5] = eeprom[6]; /* Make sure the EEPROM contains RATOC's station address. */ if (!fe_valid_Ether_p(sc->enaddr, 0x00C0D0)) return NULL; /* I don't know any sub-model identification. */ sc->type = FE_TYPE_JLI; sc->typestr = "REX-5586/5587"; /* Returns the IRQ for the RATOC board. */ return irqmap_rex; } /* JLI sub-probe for Unknown board. */ static u_short const * fe_probe_jli_unk(struct fe_softc * sc, u_char const * eeprom) { int i, n, romsize; static u_short const irqmap [4] = { NO_IRQ, NO_IRQ, NO_IRQ, NO_IRQ }; /* The generic JLI probe considered this board has an 86965 in JLI mode, but any other board-specific routines could not find the matching implementation. So, we "guess" the location by looking for a bit pattern which looks like a MAC address. */ /* Determine how large the EEPROM is. */ for (romsize = JLI_EEPROM_SIZE/2; romsize > 16; romsize >>= 1) { for (i = 0; i < romsize; i++) { if (eeprom[i] != eeprom[i+romsize]) break; } if (i < romsize) break; } romsize <<= 1; /* Look for a bit pattern which looks like a MAC address. */ for (n = 2; n <= romsize - ETHER_ADDR_LEN; n += 2) { if (!fe_valid_Ether_p(eeprom + n, 0x000000)) continue; } /* If no reasonable address was found, we can't go further. */ if (n > romsize - ETHER_ADDR_LEN) return NULL; /* Extract our (guessed) station address. */ bcopy(eeprom+n, sc->enaddr, ETHER_ADDR_LEN); /* We are not sure what type of board it is... */ sc->type = FE_TYPE_JLI; sc->typestr = "(unknown JLI)"; sc->stability |= UNSTABLE_TYPE | UNSTABLE_MAC; /* Returns the totally unknown IRQ mapping table. */ return irqmap; } /* * Probe and initialization for all JLI implementations. */ static int fe_probe_jli(device_t dev) { struct fe_softc *sc = device_get_softc(dev); int i, n, error, xirq; u_long iobase, irq; u_char eeprom [JLI_EEPROM_SIZE]; u_short const * irqmap; static u_short const baseaddr [8] = { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300 }; static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR1, 0x20, 0x00 }, { FE_DLCR2, 0x50, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { FE_DLCR5, 0x80, 0x00 }, #if 0 { FE_BMPR16, 0x1B, 0x00 }, { FE_BMPR17, 0x7F, 0x00 }, #endif { 0 } }; /* * See if the specified address is possible for MB86965A JLI mode. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; for (i = 0; i < 8; i++) { if (baseaddr[i] == iobase) break; } if (i == 8) return ENXIO; /* 86965 JLI occupies 32 I/O addresses. */ if (fe_alloc_port(dev, 32)) return ENXIO; /* Fill the softc struct with reasonable default. */ fe_softc_defaults(sc); /* * We should test if MB86965A is on the base address now. * Unfortunately, it is very hard to probe it reliably, since * we have no way to reset the chip under software control. * On cold boot, we could check the "signature" bit patterns * described in the Fujitsu document. On warm boot, however, * we can predict almost nothing about register values. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* Check if our I/O address matches config info on 86965. */ n = (fe_inb(sc, FE_BMPR19) & FE_B19_ADDR) >> FE_B19_ADDR_SHIFT; if (baseaddr[n] != iobase) return ENXIO; /* * We are now almost sure we have an MB86965 at the given * address. So, read EEPROM through it. We have to write * into LSI registers to read from EEPROM. I want to avoid it * at this stage, but I cannot test the presence of the chip * any further without reading EEPROM. FIXME. */ fe_read_eeprom_jli(sc, eeprom); /* Make sure that config info in EEPROM and 86965 agree. */ if (eeprom[FE_EEPROM_CONF] != fe_inb(sc, FE_BMPR19)) return ENXIO; /* Use 86965 media selection scheme, unless othewise specified. It is "AUTO always" and "select with BMPR13." This behaviour covers most of the 86965 based board (as minimum requirements.) It is backward compatible with previous versions, also. */ sc->mbitmap = MB_HA; sc->defmedia = MB_HA; sc->msel = fe_msel_965; /* Perform board-specific probe, one by one. Note that the order of probe is important and should not be changed arbitrarily. */ if ((irqmap = fe_probe_jli_ati(sc, eeprom)) == NULL && (irqmap = fe_probe_jli_rex(sc, eeprom)) == NULL && (irqmap = fe_probe_jli_icl(sc, eeprom)) == NULL && (irqmap = fe_probe_jli_unk(sc, eeprom)) == NULL) return ENXIO; /* Find the IRQ read from EEPROM. */ n = (fe_inb(sc, FE_BMPR19) & FE_B19_IRQ) >> FE_B19_IRQ_SHIFT; xirq = irqmap[n]; /* Try to determine IRQ setting. */ error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); if (error && xirq == NO_IRQ) { /* The device must be configured with an explicit IRQ. */ device_printf(dev, "IRQ auto-detection does not work\n"); return ENXIO; } else if (error && xirq != NO_IRQ) { /* Just use the probed IRQ value. */ bus_set_resource(dev, SYS_RES_IRQ, 0, xirq, 1); } else if (!error && xirq == NO_IRQ) { /* No problem. Go ahead. */ } else if (irq == xirq) { /* Good. Go ahead. */ } else { /* User must be warned in this case. */ sc->stability |= UNSTABLE_IRQ; } /* Setup a hook, which resets te 86965 when the driver is being initialized. This may solve a nasty bug. FIXME. */ sc->init = fe_init_jli; return 0; } /* Probe for TDK LAK-AX031, which is an SSi 78Q8377A based board. */ static int fe_probe_ssi(device_t dev) { struct fe_softc *sc = device_get_softc(dev); u_long iobase, irq; u_char eeprom [SSI_EEPROM_SIZE]; static struct fe_simple_probe_struct probe_table [] = { { FE_DLCR2, 0x08, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { 0 } }; /* See if the specified I/O address is possible for 78Q8377A. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; if ((iobase & ~0x3F0) != 0x000) return ENXIO; /* We have 16 registers. */ if (fe_alloc_port(dev, 16)) return ENXIO; /* Fill the softc struct with default values. */ fe_softc_defaults(sc); /* See if the card is on its address. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* We now have to read the config EEPROM. We should be very careful, since doing so destroys a register. (Remember, we are not yet sure we have a LAK-AX031 board here.) Don't remember to select BMPRs bofore reading EEPROM, since other register bank may be selected before the probe() is called. */ fe_read_eeprom_ssi(sc, eeprom); /* Make sure the Ethernet (MAC) station address is of TDK's. */ if (!fe_valid_Ether_p(eeprom+FE_SSI_EEP_ADDR, 0x008098)) return ENXIO; bcopy(eeprom + FE_SSI_EEP_ADDR, sc->enaddr, ETHER_ADDR_LEN); /* This looks like a TDK-AX031 board. It requires an explicit IRQ setting in config, since we currently don't know how we can find the IRQ value assigned by ISA PnP manager. */ if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) { fe_irq_failure("LAK-AX031", sc->sc_unit, NO_IRQ, NULL); return ENXIO; } /* Fill softc struct accordingly. */ sc->type = FE_TYPE_SSI; sc->typestr = "LAK-AX031"; sc->mbitmap = MB_HT; sc->defmedia = MB_HT; return 0; } /* * Probe and initialization for TDK/LANX LAC-AX012/013 boards. */ static int fe_probe_lnx(device_t dev) { struct fe_softc *sc = device_get_softc(dev); u_long iobase, irq; u_char eeprom [LNX_EEPROM_SIZE]; static struct fe_simple_probe_struct probe_table [] = { { FE_DLCR2, 0x58, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { 0 } }; /* See if the specified I/O address is possible for TDK/LANX boards. */ /* 300, 320, 340, and 360 are allowed. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; if ((iobase & ~0x060) != 0x300) return ENXIO; /* We have 32 registers. */ if (fe_alloc_port(dev, 32)) return ENXIO; /* Fill the softc struct with default values. */ fe_softc_defaults(sc); /* See if the card is on its address. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* We now have to read the config EEPROM. We should be very careful, since doing so destroys a register. (Remember, we are not yet sure we have a LAC-AX012/AX013 board here.) */ fe_read_eeprom_lnx(sc, eeprom); /* Make sure the Ethernet (MAC) station address is of TDK/LANX's. */ if (!fe_valid_Ether_p(eeprom, 0x008098)) return ENXIO; bcopy(eeprom, sc->enaddr, ETHER_ADDR_LEN); /* This looks like a TDK/LANX board. It requires an explicit IRQ setting in config. Make sure we have one, determining an appropriate value for the IRQ control register. */ irq = 0; bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); switch (irq) { case 3: sc->priv_info = 0x40 | LNX_CLK_LO | LNX_SDA_HI; break; case 4: sc->priv_info = 0x20 | LNX_CLK_LO | LNX_SDA_HI; break; case 5: sc->priv_info = 0x10 | LNX_CLK_LO | LNX_SDA_HI; break; case 9: sc->priv_info = 0x80 | LNX_CLK_LO | LNX_SDA_HI; break; default: fe_irq_failure("LAC-AX012/AX013", sc->sc_unit, irq, "3/4/5/9"); return ENXIO; } /* Fill softc struct accordingly. */ sc->type = FE_TYPE_LNX; sc->typestr = "LAC-AX012/AX013"; sc->init = fe_init_lnx; return 0; } /* * Probe and initialization for Gateway Communications' old cards. */ static int fe_probe_gwy(device_t dev) { struct fe_softc *sc = device_get_softc(dev); u_long iobase, irq; static struct fe_simple_probe_struct probe_table [] = { /* { FE_DLCR2, 0x70, 0x00 }, */ { FE_DLCR2, 0x58, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { 0 } }; /* See if the specified I/O address is possible for Gateway boards. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; if ((iobase & ~0x1E0) != 0x200) return ENXIO; /* That's all. The card occupies 32 I/O addresses, as always. */ if (fe_alloc_port(dev, 32)) return ENXIO; /* Setup an I/O address mapping table and some others. */ fe_softc_defaults(sc); /* See if the card is on its address. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* Get our station address from EEPROM. */ fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); /* Make sure it is Gateway Communication's. */ if (!fe_valid_Ether_p(sc->enaddr, 0x000061)) return ENXIO; /* Gateway's board requires an explicit IRQ to work, since it is not possible to probe the setting of jumpers. */ if (bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL) != 0) { fe_irq_failure("Gateway Ethernet", sc->sc_unit, NO_IRQ, NULL); return ENXIO; } /* Fill softc struct accordingly. */ sc->type = FE_TYPE_GWY; sc->typestr = "Gateway Ethernet (Fujitsu chipset)"; return 0; } /* Probe and initialization for Ungermann-Bass Network K.K. "Access/PC" boards. */ static int fe_probe_ubn(device_t dev) { struct fe_softc *sc = device_get_softc(dev); u_long iobase, irq; #if 0 u_char sum; #endif static struct fe_simple_probe_struct const probe_table [] = { { FE_DLCR2, 0x58, 0x00 }, { FE_DLCR4, 0x08, 0x00 }, { 0 } }; /* See if the specified I/O address is possible for AccessPC/ISA. */ if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &iobase, NULL) != 0) return ENXIO; if ((iobase & ~0x0E0) != 0x300) return ENXIO; /* We have 32 registers. */ if (fe_alloc_port(dev, 32)) return ENXIO; /* Setup an I/O address mapping table and some others. */ fe_softc_defaults(sc); /* Simple probe. */ if (!fe_simple_probe(sc, probe_table)) return ENXIO; /* Get our station address form ID ROM and make sure it is UBN's. */ fe_inblk(sc, 0x18, sc->enaddr, ETHER_ADDR_LEN); if (!fe_valid_Ether_p(sc->enaddr, 0x00DD01)) return ENXIO; #if 0 /* Calculate checksum. */ sum = fe_inb(sc, 0x1e); for (i = 0; i < ETHER_ADDR_LEN; i++) { sum ^= sc->enaddr[i]; } if (sum != 0) return ENXIO; #endif /* This looks like an AccessPC/ISA board. It requires an explicit IRQ setting in config. Make sure we have one, determining an appropriate value for the IRQ control register. */ irq = 0; bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, NULL); switch (irq) { case 3: sc->priv_info = 0x02; break; case 4: sc->priv_info = 0x04; break; case 5: sc->priv_info = 0x08; break; case 10: sc->priv_info = 0x10; break; default: fe_irq_failure("Access/PC", sc->sc_unit, irq, "3/4/5/10"); return ENXIO; } /* Fill softc struct accordingly. */ sc->type = FE_TYPE_UBN; sc->typestr = "Access/PC"; sc->init = fe_init_ubn; return 0; } Index: projects/building-blocks/sys/dev/gpio/gpiobus.c =================================================================== --- projects/building-blocks/sys/dev/gpio/gpiobus.c (revision 278195) +++ projects/building-blocks/sys/dev/gpio/gpiobus.c (revision 278196) @@ -1,658 +1,665 @@ /*- * Copyright (c) 2009 Oleksandr Tymoshenko * 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 #include #include #include #include #include #include #include #include "gpiobus_if.h" #undef GPIOBUS_DEBUG #ifdef GPIOBUS_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif static void gpiobus_print_pins(struct gpiobus_ivar *, char *, size_t); static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int); static int gpiobus_probe(device_t); static int gpiobus_attach(device_t); static int gpiobus_detach(device_t); static int gpiobus_suspend(device_t); static int gpiobus_resume(device_t); static int gpiobus_print_child(device_t, device_t); static int gpiobus_child_location_str(device_t, device_t, char *, size_t); static int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t); static device_t gpiobus_add_child(device_t, u_int, const char *, int); static void gpiobus_hinted_child(device_t, const char *, int); /* * GPIOBUS interface */ static int gpiobus_acquire_bus(device_t, device_t, int); static void gpiobus_release_bus(device_t, device_t); static int gpiobus_pin_setflags(device_t, device_t, uint32_t, uint32_t); static int gpiobus_pin_getflags(device_t, device_t, uint32_t, uint32_t*); static int gpiobus_pin_getcaps(device_t, device_t, uint32_t, uint32_t*); static int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned int); static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*); static int gpiobus_pin_toggle(device_t, device_t, uint32_t); int gpio_check_flags(uint32_t caps, uint32_t flags) { /* Check for unwanted flags. */ if ((flags & caps) == 0 || (flags & caps) != flags) return (EINVAL); /* Cannot mix input/output together. */ if (flags & GPIO_PIN_INPUT && flags & GPIO_PIN_OUTPUT) return (EINVAL); /* Cannot mix pull-up/pull-down together. */ if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN) return (EINVAL); return (0); } static void gpiobus_print_pins(struct gpiobus_ivar *devi, char *buf, size_t buflen) { char tmp[128]; int i, range_start, range_stop, need_coma; if (devi->npins == 0) return; need_coma = 0; range_start = range_stop = devi->pins[0]; for (i = 1; i < devi->npins; i++) { if (devi->pins[i] != (range_stop + 1)) { if (need_coma) strlcat(buf, ",", buflen); memset(tmp, 0, sizeof(tmp)); if (range_start != range_stop) snprintf(tmp, sizeof(tmp) - 1, "%d-%d", range_start, range_stop); else snprintf(tmp, sizeof(tmp) - 1, "%d", range_start); strlcat(buf, tmp, buflen); range_start = range_stop = devi->pins[i]; need_coma = 1; } else range_stop++; } if (need_coma) strlcat(buf, ",", buflen); memset(tmp, 0, sizeof(tmp)); if (range_start != range_stop) snprintf(tmp, sizeof(tmp) - 1, "%d-%d", range_start, range_stop); else snprintf(tmp, sizeof(tmp) - 1, "%d", range_start); strlcat(buf, tmp, buflen); } device_t gpiobus_attach_bus(device_t dev) { device_t busdev; busdev = device_add_child(dev, "gpiobus", -1); if (busdev == NULL) return (NULL); if (device_add_child(dev, "gpioc", -1) == NULL) { device_delete_child(dev, busdev); return (NULL); } +#ifdef FDT + ofw_gpiobus_register_provider(dev); +#endif bus_generic_attach(dev); return (busdev); } int gpiobus_detach_bus(device_t dev) { + +#ifdef FDT + ofw_gpiobus_unregister_provider(dev); +#endif return (bus_generic_detach(dev)); } int gpiobus_init_softc(device_t dev) { struct gpiobus_softc *sc; sc = GPIOBUS_SOFTC(dev); sc->sc_busdev = dev; sc->sc_dev = device_get_parent(dev); sc->sc_intr_rman.rm_type = RMAN_ARRAY; sc->sc_intr_rman.rm_descr = "GPIO Interrupts"; if (rman_init(&sc->sc_intr_rman) != 0 || rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0) panic("%s: failed to set up rman.", __func__); if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0) return (ENXIO); KASSERT(sc->sc_npins != 0, ("GPIO device with no pins")); /* Pins = GPIO_PIN_MAX() + 1 */ sc->sc_npins++; sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_pins_mapped == NULL) return (ENOMEM); /* Initialize the bus lock. */ GPIOBUS_LOCK_INIT(sc); return (0); } static int gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask) { struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); int i, npins; npins = 0; for (i = 0; i < 32; i++) { if (mask & (1 << i)) npins++; } if (npins == 0) { device_printf(child, "empty pin mask\n"); return (EINVAL); } devi->npins = npins; devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, M_NOWAIT | M_ZERO); if (!devi->pins) return (ENOMEM); npins = 0; for (i = 0; i < 32; i++) { if ((mask & (1 << i)) == 0) continue; if (i >= sc->sc_npins) { device_printf(child, "invalid pin %d, max: %d\n", i, sc->sc_npins - 1); free(devi->pins, M_DEVBUF); return (EINVAL); } devi->pins[npins++] = i; /* * Mark pin as mapped and give warning if it's already mapped */ if (sc->sc_pins_mapped[i]) { device_printf(child, "warning: pin %d is already mapped\n", i); free(devi->pins, M_DEVBUF); return (EINVAL); } sc->sc_pins_mapped[i] = 1; } return (0); } static int gpiobus_probe(device_t dev) { device_set_desc(dev, "GPIO bus"); return (BUS_PROBE_GENERIC); } static int gpiobus_attach(device_t dev) { int err; err = gpiobus_init_softc(dev); if (err != 0) return (err); /* * Get parent's pins and mark them as unmapped */ bus_generic_probe(dev); bus_enumerate_hinted_children(dev); return (bus_generic_attach(dev)); } /* * Since this is not a self-enumerating bus, and since we always add * children in attach, we have to always delete children here. */ static int gpiobus_detach(device_t dev) { struct gpiobus_softc *sc; struct gpiobus_ivar *devi; device_t *devlist; int i, err, ndevs; sc = GPIOBUS_SOFTC(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpiobus mutex not initialized")); GPIOBUS_LOCK_DESTROY(sc); if ((err = bus_generic_detach(dev)) != 0) return (err); if ((err = device_get_children(dev, &devlist, &ndevs)) != 0) return (err); for (i = 0; i < ndevs; i++) { device_delete_child(dev, devlist[i]); devi = GPIOBUS_IVAR(devlist[i]); if (devi->pins) { free(devi->pins, M_DEVBUF); devi->pins = NULL; } } free(devlist, M_TEMP); if (sc->sc_pins_mapped) { free(sc->sc_pins_mapped, M_DEVBUF); sc->sc_pins_mapped = NULL; } return (0); } static int gpiobus_suspend(device_t dev) { return (bus_generic_suspend(dev)); } static int gpiobus_resume(device_t dev) { return (bus_generic_resume(dev)); } static int gpiobus_print_child(device_t dev, device_t child) { char pins[128]; int retval = 0; struct gpiobus_ivar *devi; devi = GPIOBUS_IVAR(child); memset(pins, 0, sizeof(pins)); retval += bus_print_child_header(dev, child); retval += printf(" at pin(s) "); gpiobus_print_pins(devi, pins, sizeof(pins)); retval += printf("%s", pins); resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); retval += bus_print_child_footer(dev, child); return (retval); } static int gpiobus_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { struct gpiobus_ivar *devi; devi = GPIOBUS_IVAR(child); strlcpy(buf, "pin(s)=", buflen); gpiobus_print_pins(devi, buf, buflen); return (0); } static int gpiobus_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { *buf = '\0'; return (0); } static device_t gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct gpiobus_ivar *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } resource_list_init(&devi->rl); device_set_ivars(child, devi); return (child); } static void gpiobus_hinted_child(device_t bus, const char *dname, int dunit) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus); struct gpiobus_ivar *devi; device_t child; int irq, pins; child = BUS_ADD_CHILD(bus, 0, dname, dunit); devi = GPIOBUS_IVAR(child); resource_int_value(dname, dunit, "pins", &pins); if (gpiobus_parse_pins(sc, child, pins)) device_delete_child(bus, child); if (resource_int_value(dname, dunit, "irq", &irq) == 0) { if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) device_printf(bus, "warning: bus_set_resource() failed\n"); } } static int gpiobus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct gpiobus_ivar *devi; struct resource_list_entry *rle; dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n", __func__, dev, child, type, rid, (void *)(intptr_t)start, count); devi = GPIOBUS_IVAR(child); rle = resource_list_add(&devi->rl, type, rid, start, start + count - 1, count); if (rle == NULL) return (ENXIO); return (0); } static struct resource * gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct gpiobus_softc *sc; struct resource *rv; struct resource_list *rl; struct resource_list_entry *rle; int isdefault; if (type != SYS_RES_IRQ) return (NULL); isdefault = (start == 0UL && end == ~0UL && count == 1); rle = NULL; if (isdefault) { rl = BUS_GET_RESOURCE_LIST(bus, child); if (rl == NULL) return (NULL); rle = resource_list_find(rl, type, *rid); if (rle == NULL) return (NULL); if (rle->res != NULL) panic("%s: resource entry is busy", __func__); start = rle->start; count = rle->count; end = rle->end; } sc = device_get_softc(bus); rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags, child); if (rv == NULL) return (NULL); rman_set_rid(rv, *rid); if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type, *rid, rv) != 0) { rman_release_resource(rv); return (NULL); } return (rv); } static int gpiobus_release_resource(device_t bus __unused, device_t child, int type, int rid, struct resource *r) { int error; if (rman_get_flags(r) & RF_ACTIVE) { error = bus_deactivate_resource(child, type, rid, r); if (error) return (error); } return (rman_release_resource(r)); } static struct resource_list * gpiobus_get_resource_list(device_t bus __unused, device_t child) { struct gpiobus_ivar *ivar; ivar = GPIOBUS_IVAR(child); return (&ivar->rl); } static int gpiobus_acquire_bus(device_t busdev, device_t child, int how) { struct gpiobus_softc *sc; sc = device_get_softc(busdev); GPIOBUS_ASSERT_UNLOCKED(sc); GPIOBUS_LOCK(sc); if (sc->sc_owner != NULL) { if (how == GPIOBUS_DONTWAIT) { GPIOBUS_UNLOCK(sc); return (EWOULDBLOCK); } while (sc->sc_owner != NULL) mtx_sleep(sc, &sc->sc_mtx, 0, "gpiobuswait", 0); } sc->sc_owner = child; GPIOBUS_UNLOCK(sc); return (0); } static void gpiobus_release_bus(device_t busdev, device_t child) { struct gpiobus_softc *sc; sc = device_get_softc(busdev); GPIOBUS_ASSERT_UNLOCKED(sc); GPIOBUS_LOCK(sc); if (sc->sc_owner == NULL) panic("gpiobus: releasing unowned bus."); if (sc->sc_owner != child) panic("gpiobus: you don't own the bus. game over."); sc->sc_owner = NULL; wakeup(sc); GPIOBUS_UNLOCK(sc); } static int gpiobus_pin_setflags(device_t dev, device_t child, uint32_t pin, uint32_t flags) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); uint32_t caps; if (pin >= devi->npins) return (EINVAL); if (GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], &caps) != 0) return (EINVAL); if (gpio_check_flags(caps, flags) != 0) return (EINVAL); return (GPIO_PIN_SETFLAGS(sc->sc_dev, devi->pins[pin], flags)); } static int gpiobus_pin_getflags(device_t dev, device_t child, uint32_t pin, uint32_t *flags) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GETFLAGS(sc->sc_dev, devi->pins[pin], flags); } static int gpiobus_pin_getcaps(device_t dev, device_t child, uint32_t pin, uint32_t *caps) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], caps); } static int gpiobus_pin_set(device_t dev, device_t child, uint32_t pin, unsigned int value) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_SET(sc->sc_dev, devi->pins[pin], value); } static int gpiobus_pin_get(device_t dev, device_t child, uint32_t pin, unsigned int *value) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_GET(sc->sc_dev, devi->pins[pin], value); } static int gpiobus_pin_toggle(device_t dev, device_t child, uint32_t pin) { struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev); struct gpiobus_ivar *devi = GPIOBUS_IVAR(child); if (pin >= devi->npins) return (EINVAL); return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]); } static device_method_t gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gpiobus_probe), DEVMETHOD(device_attach, gpiobus_attach), DEVMETHOD(device_detach, gpiobus_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, gpiobus_suspend), DEVMETHOD(device_resume, gpiobus_resume), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_config_intr, bus_generic_config_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_set_resource, gpiobus_set_resource), DEVMETHOD(bus_alloc_resource, gpiobus_alloc_resource), DEVMETHOD(bus_release_resource, gpiobus_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list), DEVMETHOD(bus_add_child, gpiobus_add_child), DEVMETHOD(bus_print_child, gpiobus_print_child), DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, gpiobus_child_location_str), DEVMETHOD(bus_hinted_child, gpiobus_hinted_child), /* GPIO protocol */ DEVMETHOD(gpiobus_acquire_bus, gpiobus_acquire_bus), DEVMETHOD(gpiobus_release_bus, gpiobus_release_bus), DEVMETHOD(gpiobus_pin_getflags, gpiobus_pin_getflags), DEVMETHOD(gpiobus_pin_getcaps, gpiobus_pin_getcaps), DEVMETHOD(gpiobus_pin_setflags, gpiobus_pin_setflags), DEVMETHOD(gpiobus_pin_get, gpiobus_pin_get), DEVMETHOD(gpiobus_pin_set, gpiobus_pin_set), DEVMETHOD(gpiobus_pin_toggle, gpiobus_pin_toggle), DEVMETHOD_END }; driver_t gpiobus_driver = { "gpiobus", gpiobus_methods, sizeof(struct gpiobus_softc) }; devclass_t gpiobus_devclass; DRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0); MODULE_VERSION(gpiobus, 1); Index: projects/building-blocks/sys/dev/gpio/gpiobusvar.h =================================================================== --- projects/building-blocks/sys/dev/gpio/gpiobusvar.h (revision 278195) +++ projects/building-blocks/sys/dev/gpio/gpiobusvar.h (revision 278196) @@ -1,104 +1,106 @@ /*- * Copyright (c) 2009 Oleksandr Tymoshenko * 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 __GPIOBUS_H__ #define __GPIOBUS_H__ #include "opt_platform.h" #include #include #include #ifdef FDT #include #endif #include "gpio_if.h" #ifdef FDT #define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) \ &((struct ofw_gpiobus_devinfo *)device_get_ivars(d))->opd_dinfo #else #define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d) #endif #define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d) #define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define GPIOBUS_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "gpiobus", MTX_DEF) #define GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx) #define GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED) #define GPIOBUS_WAIT 1 #define GPIOBUS_DONTWAIT 2 struct gpiobus_softc { struct mtx sc_mtx; /* bus mutex */ struct rman sc_intr_rman; /* isr resources */ device_t sc_busdev; /* bus device */ device_t sc_owner; /* bus owner */ device_t sc_dev; /* driver device */ int sc_npins; /* total pins on bus */ int *sc_pins_mapped; /* mark mapped pins */ }; struct gpiobus_ivar { struct resource_list rl; /* isr resource list */ uint32_t npins; /* pins total */ uint32_t *flags; /* pins flags */ uint32_t *pins; /* pins map */ }; #ifdef FDT struct ofw_gpiobus_devinfo { struct gpiobus_ivar opd_dinfo; struct ofw_bus_devinfo opd_obdinfo; }; static __inline int gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { return (GPIO_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin, flags)); } device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t); +void ofw_gpiobus_register_provider(device_t); +void ofw_gpiobus_unregister_provider(device_t); #endif int gpio_check_flags(uint32_t, uint32_t); device_t gpiobus_attach_bus(device_t); int gpiobus_detach_bus(device_t); int gpiobus_init_softc(device_t); extern driver_t gpiobus_driver; #endif /* __GPIOBUS_H__ */ Index: projects/building-blocks/sys/dev/gpio/ofw_gpiobus.c =================================================================== --- projects/building-blocks/sys/dev/gpio/ofw_gpiobus.c (revision 278195) +++ projects/building-blocks/sys/dev/gpio/ofw_gpiobus.c (revision 278196) @@ -1,363 +1,381 @@ /*- * Copyright (c) 2009, Nathan Whitehorn * Copyright (c) 2013, Luiz Otavio O Souza * Copyright (c) 2013 The FreeBSD Foundation * 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 static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *, struct gpiobus_ivar *, phandle_t); static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t, phandle_t); static void ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *); device_t ofw_gpiobus_add_fdt_child(device_t bus, phandle_t child) { struct ofw_gpiobus_devinfo *dinfo; device_t childdev; /* * Set up the GPIO child and OFW bus layer devinfo and add it to bus. */ dinfo = ofw_gpiobus_setup_devinfo(bus, child); if (dinfo == NULL) return (NULL); childdev = device_add_child(bus, NULL, -1); if (childdev == NULL) { device_printf(bus, "could not add child: %s\n", dinfo->opd_obdinfo.obd_name); ofw_gpiobus_destroy_devinfo(dinfo); return (NULL); } device_set_ivars(childdev, dinfo); return (childdev); } static int ofw_gpiobus_alloc_ivars(struct gpiobus_ivar *dinfo) { /* Allocate pins and flags memory. */ dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF, M_NOWAIT | M_ZERO); if (dinfo->pins == NULL) return (ENOMEM); dinfo->flags = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF, M_NOWAIT | M_ZERO); if (dinfo->flags == NULL) { free(dinfo->pins, M_DEVBUF); return (ENOMEM); } return (0); } static void ofw_gpiobus_free_ivars(struct gpiobus_ivar *dinfo) { free(dinfo->flags, M_DEVBUF); free(dinfo->pins, M_DEVBUF); } static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo, phandle_t child) { int cells, i, j, len; pcell_t *gpios; phandle_t gpio; /* Retrieve the gpios property. */ if ((len = OF_getproplen(child, "gpios")) < 0) return (EINVAL); gpios = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); if (gpios == NULL) return (ENOMEM); if (OF_getencprop(child, "gpios", gpios, len) < 0) { free(gpios, M_DEVBUF); return (EINVAL); } /* * The gpio-specifier is controller independent, but the first pcell * has the reference to the GPIO controller phandler. * One the first pass we count the number of encoded gpio-specifiers. */ i = 0; len /= sizeof(pcell_t); while (i < len) { /* Allow NULL specifiers. */ if (gpios[i] == 0) { dinfo->npins++; i++; continue; } gpio = OF_node_from_xref(gpios[i]); /* Verify if we're attaching to the correct GPIO controller. */ if (!OF_hasprop(gpio, "gpio-controller") || gpio != ofw_bus_get_node(sc->sc_dev)) { free(gpios, M_DEVBUF); return (EINVAL); } /* Read gpio-cells property for this GPIO controller. */ if (OF_getencprop(gpio, "#gpio-cells", &cells, sizeof(cells)) < 0) { free(gpios, M_DEVBUF); return (EINVAL); } dinfo->npins++; i += cells + 1; } if (dinfo->npins == 0) { free(gpios, M_DEVBUF); return (EINVAL); } /* Allocate the child resources. */ if (ofw_gpiobus_alloc_ivars(dinfo) != 0) { free(gpios, M_DEVBUF); return (ENOMEM); } /* Decode the gpio specifier on the second pass. */ i = 0; j = 0; while (i < len) { /* Allow NULL specifiers. */ if (gpios[i] == 0) { i++; j++; continue; } gpio = OF_node_from_xref(gpios[i]); /* Read gpio-cells property for this GPIO controller. */ if (OF_getencprop(gpio, "#gpio-cells", &cells, sizeof(cells)) < 0) { ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } /* Get the GPIO pin number and flags. */ if (gpio_map_gpios(sc->sc_dev, child, gpio, cells, &gpios[i + 1], &dinfo->pins[j], &dinfo->flags[j]) != 0) { ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } /* Consistency check. */ if (dinfo->pins[j] > sc->sc_npins) { device_printf(sc->sc_busdev, "invalid pin %d, max: %d\n", dinfo->pins[j], sc->sc_npins - 1); ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } /* * Mark pin as mapped and give warning if it's already mapped. */ if (sc->sc_pins_mapped[dinfo->pins[j]]) { device_printf(sc->sc_busdev, "warning: pin %d is already mapped\n", dinfo->pins[j]); ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } sc->sc_pins_mapped[dinfo->pins[j]] = 1; i += cells + 1; j++; } free(gpios, M_DEVBUF); return (0); } +void +ofw_gpiobus_register_provider(device_t provider) +{ + phandle_t node; + + node = ofw_bus_get_node(provider); + OF_device_register_xref(OF_xref_from_node(node), provider); +} + +void +ofw_gpiobus_unregister_provider(device_t provider) +{ + phandle_t node; + + node = ofw_bus_get_node(provider); + OF_device_register_xref(OF_xref_from_node(node), NULL); +} + static struct ofw_gpiobus_devinfo * ofw_gpiobus_setup_devinfo(device_t dev, phandle_t node) { struct gpiobus_softc *sc; struct ofw_gpiobus_devinfo *dinfo; sc = device_get_softc(dev); dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (dinfo == NULL) return (NULL); if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) { free(dinfo, M_DEVBUF); return (NULL); } /* Parse the gpios property for the child. */ if (ofw_gpiobus_parse_gpios(sc, &dinfo->opd_dinfo, node) != 0) { ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); free(dinfo, M_DEVBUF); return (NULL); } /* And now the interrupt resources. */ resource_list_init(&dinfo->opd_dinfo.rl); if (ofw_bus_intr_to_rl(dev, node, &dinfo->opd_dinfo.rl) != 0) { ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); free(dinfo, M_DEVBUF); return (NULL); } return (dinfo); } static void ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo) { resource_list_free(&dinfo->opd_dinfo.rl); ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); free(dinfo, M_DEVBUF); } static int ofw_gpiobus_probe(device_t dev) { if (ofw_bus_get_node(dev) == -1) return (ENXIO); device_set_desc(dev, "OFW GPIO bus"); return (0); } static int ofw_gpiobus_attach(device_t dev) { int err; phandle_t child; err = gpiobus_init_softc(dev); if (err != 0) return (err); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); /* * Attach the children represented in the device tree. */ for (child = OF_child(ofw_bus_get_node(dev)); child != 0; child = OF_peer(child)) if (ofw_gpiobus_add_fdt_child(dev, child) == NULL) continue; return (bus_generic_attach(dev)); } static device_t ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct ofw_gpiobus_devinfo *devi; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (0); } /* * NULL all the OFW-related parts of the ivars for non-OFW * children. */ devi->opd_obdinfo.obd_node = -1; devi->opd_obdinfo.obd_name = NULL; devi->opd_obdinfo.obd_compat = NULL; devi->opd_obdinfo.obd_type = NULL; devi->opd_obdinfo.obd_model = NULL; device_set_ivars(child, devi); return (child); } static const struct ofw_bus_devinfo * ofw_gpiobus_get_devinfo(device_t bus, device_t dev) { struct ofw_gpiobus_devinfo *dinfo; dinfo = device_get_ivars(dev); return (&dinfo->opd_obdinfo); } static device_method_t ofw_gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ofw_gpiobus_probe), DEVMETHOD(device_attach, ofw_gpiobus_attach), /* Bus interface */ DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), DEVMETHOD(bus_add_child, ofw_gpiobus_add_child), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, ofw_gpiobus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; static devclass_t ofwgpiobus_devclass; DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods, sizeof(struct gpiobus_softc), gpiobus_driver); DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0); MODULE_VERSION(ofw_gpiobus, 1); MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1); Index: projects/building-blocks/sys/dev/iscsi/iscsi_proto.h =================================================================== --- projects/building-blocks/sys/dev/iscsi/iscsi_proto.h (revision 278195) +++ projects/building-blocks/sys/dev/iscsi/iscsi_proto.h (revision 278196) @@ -1,442 +1,456 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala 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 ISCSI_PROTO_H #define ISCSI_PROTO_H #ifndef CTASSERT #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] #endif #define ISCSI_SNGT(x, y) ((int32_t)(x) - (int32_t)(y) > 0) #define ISCSI_SNLT(x, y) ((int32_t)(x) - (int32_t)(y) < 0) #define ISCSI_BHS_SIZE 48 #define ISCSI_HEADER_DIGEST_SIZE 4 #define ISCSI_DATA_DIGEST_SIZE 4 #define ISCSI_BHS_OPCODE_IMMEDIATE 0x40 #define ISCSI_BHS_OPCODE_NOP_OUT 0x00 #define ISCSI_BHS_OPCODE_SCSI_COMMAND 0x01 #define ISCSI_BHS_OPCODE_TASK_REQUEST 0x02 #define ISCSI_BHS_OPCODE_LOGIN_REQUEST 0x03 #define ISCSI_BHS_OPCODE_TEXT_REQUEST 0x04 #define ISCSI_BHS_OPCODE_SCSI_DATA_OUT 0x05 #define ISCSI_BHS_OPCODE_LOGOUT_REQUEST 0x06 #define ISCSI_BHS_OPCODE_NOP_IN 0x20 #define ISCSI_BHS_OPCODE_SCSI_RESPONSE 0x21 #define ISCSI_BHS_OPCODE_TASK_RESPONSE 0x22 #define ISCSI_BHS_OPCODE_LOGIN_RESPONSE 0x23 #define ISCSI_BHS_OPCODE_TEXT_RESPONSE 0x24 #define ISCSI_BHS_OPCODE_SCSI_DATA_IN 0x25 #define ISCSI_BHS_OPCODE_LOGOUT_RESPONSE 0x26 #define ISCSI_BHS_OPCODE_R2T 0x31 #define ISCSI_BHS_OPCODE_ASYNC_MESSAGE 0x32 #define ISCSI_BHS_OPCODE_REJECT 0x3f struct iscsi_bhs { uint8_t bhs_opcode; uint8_t bhs_opcode_specific1[3]; uint8_t bhs_total_ahs_len; uint8_t bhs_data_segment_len[3]; uint64_t bhs_lun; uint8_t bhs_inititator_task_tag[4]; uint8_t bhs_opcode_specific4[28]; }; CTASSERT(sizeof(struct iscsi_bhs) == ISCSI_BHS_SIZE); #define BHSSC_FLAGS_F 0x80 #define BHSSC_FLAGS_R 0x40 #define BHSSC_FLAGS_W 0x20 #define BHSSC_FLAGS_ATTR 0x07 #define BHSSC_FLAGS_ATTR_UNTAGGED 0 #define BHSSC_FLAGS_ATTR_SIMPLE 1 #define BHSSC_FLAGS_ATTR_ORDERED 2 #define BHSSC_FLAGS_ATTR_HOQ 3 #define BHSSC_FLAGS_ATTR_ACA 4 struct iscsi_bhs_scsi_command { uint8_t bhssc_opcode; uint8_t bhssc_flags; uint8_t bhssc_reserved[2]; uint8_t bhssc_total_ahs_len; uint8_t bhssc_data_segment_len[3]; uint64_t bhssc_lun; uint32_t bhssc_initiator_task_tag; uint32_t bhssc_expected_data_transfer_length; uint32_t bhssc_cmdsn; uint32_t bhssc_expstatsn; uint8_t bhssc_cdb[16]; }; CTASSERT(sizeof(struct iscsi_bhs_scsi_command) == ISCSI_BHS_SIZE); #define BHSSR_FLAGS_RESIDUAL_UNDERFLOW 0x02 #define BHSSR_FLAGS_RESIDUAL_OVERFLOW 0x04 #define BHSSR_RESPONSE_COMMAND_COMPLETED 0x00 struct iscsi_bhs_scsi_response { uint8_t bhssr_opcode; uint8_t bhssr_flags; uint8_t bhssr_response; uint8_t bhssr_status; uint8_t bhssr_total_ahs_len; uint8_t bhssr_data_segment_len[3]; - uint64_t bhssr_reserved; + uint16_t bhssr_status_qualifier; + uint16_t bhssr_reserved; + uint32_t bhssr_reserved2; uint32_t bhssr_initiator_task_tag; uint32_t bhssr_snack_tag; uint32_t bhssr_statsn; uint32_t bhssr_expcmdsn; uint32_t bhssr_maxcmdsn; uint32_t bhssr_expdatasn; uint32_t bhssr_bidirectional_read_residual_count; uint32_t bhssr_residual_count; }; CTASSERT(sizeof(struct iscsi_bhs_scsi_response) == ISCSI_BHS_SIZE); #define BHSTMR_FUNCTION_ABORT_TASK 1 #define BHSTMR_FUNCTION_ABORT_TASK_SET 2 #define BHSTMR_FUNCTION_CLEAR_ACA 3 #define BHSTMR_FUNCTION_CLEAR_TASK_SET 4 #define BHSTMR_FUNCTION_LOGICAL_UNIT_RESET 5 #define BHSTMR_FUNCTION_TARGET_WARM_RESET 6 #define BHSTMR_FUNCTION_TARGET_COLD_RESET 7 #define BHSTMR_FUNCTION_TASK_REASSIGN 8 +#define BHSTMR_FUNCTION_QUERY_TASK 9 +#define BHSTMR_FUNCTION_QUERY_TASK_SET 10 +#define BHSTMR_FUNCTION_I_T_NEXUS_RESET 11 +#define BHSTMR_FUNCTION_QUERY_ASYNC_EVENT 12 struct iscsi_bhs_task_management_request { uint8_t bhstmr_opcode; uint8_t bhstmr_function; uint8_t bhstmr_reserved[2]; uint8_t bhstmr_total_ahs_len; uint8_t bhstmr_data_segment_len[3]; uint64_t bhstmr_lun; uint32_t bhstmr_initiator_task_tag; uint32_t bhstmr_referenced_task_tag; uint32_t bhstmr_cmdsn; uint32_t bhstmr_expstatsn; uint32_t bhstmr_refcmdsn; uint32_t bhstmr_expdatasn; uint64_t bhstmr_reserved2; }; CTASSERT(sizeof(struct iscsi_bhs_task_management_request) == ISCSI_BHS_SIZE); #define BHSTMR_RESPONSE_FUNCTION_COMPLETE 0 +#define BHSTMR_RESPONSE_TASK_DOES_NOT_EXIST 1 +#define BHSTMR_RESPONSE_LUN_DOES_NOT_EXIST 2 +#define BHSTMR_RESPONSE_TASK_STILL_ALLEGIANT 3 +#define BHSTMR_RESPONSE_TASK_ALL_REASS_NOT_SUPP 4 #define BHSTMR_RESPONSE_FUNCTION_NOT_SUPPORTED 5 +#define BHSTMR_RESPONSE_FUNCTION_AUTH_FAIL 6 +#define BHSTMR_RESPONSE_FUNCTION_SUCCEEDED 7 +#define BHSTMR_RESPONSE_FUNCTION_REJECTED 255 struct iscsi_bhs_task_management_response { uint8_t bhstmr_opcode; uint8_t bhstmr_flags; uint8_t bhstmr_response; uint8_t bhstmr_reserved; uint8_t bhstmr_total_ahs_len; uint8_t bhstmr_data_segment_len[3]; - uint64_t bhstmr_reserved2; + uint8_t bhstmr_additional_reponse_information[3]; + uint8_t bhstmr_reserved2[5]; uint32_t bhstmr_initiator_task_tag; uint32_t bhstmr_reserved3; uint32_t bhstmr_statsn; uint32_t bhstmr_expcmdsn; uint32_t bhstmr_maxcmdsn; uint8_t bhstmr_reserved4[12]; }; CTASSERT(sizeof(struct iscsi_bhs_task_management_response) == ISCSI_BHS_SIZE); #define BHSLR_FLAGS_TRANSIT 0x80 #define BHSLR_FLAGS_CONTINUE 0x40 #define BHSLR_STAGE_SECURITY_NEGOTIATION 0 #define BHSLR_STAGE_OPERATIONAL_NEGOTIATION 1 #define BHSLR_STAGE_FULL_FEATURE_PHASE 3 /* Yes, 3. */ struct iscsi_bhs_login_request { uint8_t bhslr_opcode; uint8_t bhslr_flags; uint8_t bhslr_version_max; uint8_t bhslr_version_min; uint8_t bhslr_total_ahs_len; uint8_t bhslr_data_segment_len[3]; uint8_t bhslr_isid[6]; uint16_t bhslr_tsih; uint32_t bhslr_initiator_task_tag; uint16_t bhslr_cid; uint16_t bhslr_reserved; uint32_t bhslr_cmdsn; uint32_t bhslr_expstatsn; uint8_t bhslr_reserved2[16]; }; CTASSERT(sizeof(struct iscsi_bhs_login_request) == ISCSI_BHS_SIZE); struct iscsi_bhs_login_response { uint8_t bhslr_opcode; uint8_t bhslr_flags; uint8_t bhslr_version_max; uint8_t bhslr_version_active; uint8_t bhslr_total_ahs_len; uint8_t bhslr_data_segment_len[3]; uint8_t bhslr_isid[6]; uint16_t bhslr_tsih; uint32_t bhslr_initiator_task_tag; uint32_t bhslr_reserved; uint32_t bhslr_statsn; uint32_t bhslr_expcmdsn; uint32_t bhslr_maxcmdsn; uint8_t bhslr_status_class; uint8_t bhslr_status_detail; uint16_t bhslr_reserved2; uint8_t bhslr_reserved3[8]; }; CTASSERT(sizeof(struct iscsi_bhs_login_response) == ISCSI_BHS_SIZE); #define BHSTR_FLAGS_FINAL 0x80 #define BHSTR_FLAGS_CONTINUE 0x40 struct iscsi_bhs_text_request { uint8_t bhstr_opcode; uint8_t bhstr_flags; uint16_t bhstr_reserved; uint8_t bhstr_total_ahs_len; uint8_t bhstr_data_segment_len[3]; uint64_t bhstr_lun; uint32_t bhstr_initiator_task_tag; uint32_t bhstr_target_transfer_tag; uint32_t bhstr_cmdsn; uint32_t bhstr_expstatsn; uint8_t bhstr_reserved2[16]; }; CTASSERT(sizeof(struct iscsi_bhs_text_request) == ISCSI_BHS_SIZE); struct iscsi_bhs_text_response { uint8_t bhstr_opcode; uint8_t bhstr_flags; uint16_t bhstr_reserved; uint8_t bhstr_total_ahs_len; uint8_t bhstr_data_segment_len[3]; uint64_t bhstr_lun; uint32_t bhstr_initiator_task_tag; uint32_t bhstr_target_transfer_tag; uint32_t bhstr_statsn; uint32_t bhstr_expcmdsn; uint32_t bhstr_maxcmdsn; uint8_t bhstr_reserved2[12]; }; CTASSERT(sizeof(struct iscsi_bhs_text_response) == ISCSI_BHS_SIZE); #define BHSDO_FLAGS_F 0x80 struct iscsi_bhs_data_out { uint8_t bhsdo_opcode; uint8_t bhsdo_flags; uint8_t bhsdo_reserved[2]; uint8_t bhsdo_total_ahs_len; uint8_t bhsdo_data_segment_len[3]; uint64_t bhsdo_lun; uint32_t bhsdo_initiator_task_tag; uint32_t bhsdo_target_transfer_tag; uint32_t bhsdo_reserved2; uint32_t bhsdo_expstatsn; uint32_t bhsdo_reserved3; uint32_t bhsdo_datasn; uint32_t bhsdo_buffer_offset; uint32_t bhsdo_reserved4; }; CTASSERT(sizeof(struct iscsi_bhs_data_out) == ISCSI_BHS_SIZE); #define BHSDI_FLAGS_F 0x80 #define BHSDI_FLAGS_A 0x40 #define BHSDI_FLAGS_O 0x04 #define BHSDI_FLAGS_U 0x02 #define BHSDI_FLAGS_S 0x01 struct iscsi_bhs_data_in { uint8_t bhsdi_opcode; uint8_t bhsdi_flags; uint8_t bhsdi_reserved; uint8_t bhsdi_status; uint8_t bhsdi_total_ahs_len; uint8_t bhsdi_data_segment_len[3]; uint64_t bhsdi_lun; uint32_t bhsdi_initiator_task_tag; uint32_t bhsdi_target_transfer_tag; uint32_t bhsdi_statsn; uint32_t bhsdi_expcmdsn; uint32_t bhsdi_maxcmdsn; uint32_t bhsdi_datasn; uint32_t bhsdi_buffer_offset; uint32_t bhsdi_residual_count; }; CTASSERT(sizeof(struct iscsi_bhs_data_in) == ISCSI_BHS_SIZE); struct iscsi_bhs_r2t { uint8_t bhsr2t_opcode; uint8_t bhsr2t_flags; uint16_t bhsr2t_reserved; uint8_t bhsr2t_total_ahs_len; uint8_t bhsr2t_data_segment_len[3]; uint64_t bhsr2t_lun; uint32_t bhsr2t_initiator_task_tag; uint32_t bhsr2t_target_transfer_tag; uint32_t bhsr2t_statsn; uint32_t bhsr2t_expcmdsn; uint32_t bhsr2t_maxcmdsn; uint32_t bhsr2t_r2tsn; uint32_t bhsr2t_buffer_offset; uint32_t bhsr2t_desired_data_transfer_length; }; CTASSERT(sizeof(struct iscsi_bhs_r2t) == ISCSI_BHS_SIZE); struct iscsi_bhs_nop_out { uint8_t bhsno_opcode; uint8_t bhsno_flags; uint16_t bhsno_reserved; uint8_t bhsno_total_ahs_len; uint8_t bhsno_data_segment_len[3]; uint64_t bhsno_lun; uint32_t bhsno_initiator_task_tag; uint32_t bhsno_target_transfer_tag; uint32_t bhsno_cmdsn; uint32_t bhsno_expstatsn; uint8_t bhsno_reserved2[16]; }; CTASSERT(sizeof(struct iscsi_bhs_nop_out) == ISCSI_BHS_SIZE); struct iscsi_bhs_nop_in { uint8_t bhsni_opcode; uint8_t bhsni_flags; uint16_t bhsni_reserved; uint8_t bhsni_total_ahs_len; uint8_t bhsni_data_segment_len[3]; uint64_t bhsni_lun; uint32_t bhsni_initiator_task_tag; uint32_t bhsni_target_transfer_tag; uint32_t bhsni_statsn; uint32_t bhsni_expcmdsn; uint32_t bhsni_maxcmdsn; uint8_t bhsno_reserved2[12]; }; CTASSERT(sizeof(struct iscsi_bhs_nop_in) == ISCSI_BHS_SIZE); #define BHSLR_REASON_CLOSE_SESSION 0 #define BHSLR_REASON_CLOSE_CONNECTION 1 #define BHSLR_REASON_REMOVE_FOR_RECOVERY 2 struct iscsi_bhs_logout_request { uint8_t bhslr_opcode; uint8_t bhslr_reason; uint16_t bhslr_reserved; uint8_t bhslr_total_ahs_len; uint8_t bhslr_data_segment_len[3]; uint64_t bhslr_reserved2; uint32_t bhslr_initiator_task_tag; uint16_t bhslr_cid; uint16_t bhslr_reserved3; uint32_t bhslr_cmdsn; uint32_t bhslr_expstatsn; uint8_t bhslr_reserved4[16]; }; CTASSERT(sizeof(struct iscsi_bhs_logout_request) == ISCSI_BHS_SIZE); #define BHSLR_RESPONSE_CLOSED_SUCCESSFULLY 0 #define BHSLR_RESPONSE_RECOVERY_NOT_SUPPORTED 2 struct iscsi_bhs_logout_response { uint8_t bhslr_opcode; uint8_t bhslr_flags; uint8_t bhslr_response; uint8_t bhslr_reserved; uint8_t bhslr_total_ahs_len; uint8_t bhslr_data_segment_len[3]; uint64_t bhslr_reserved2; uint32_t bhslr_initiator_task_tag; uint32_t bhslr_reserved3; uint32_t bhslr_statsn; uint32_t bhslr_expcmdsn; uint32_t bhslr_maxcmdsn; uint32_t bhslr_reserved4; uint16_t bhslr_time2wait; uint16_t bhslr_time2retain; uint32_t bhslr_reserved5; }; CTASSERT(sizeof(struct iscsi_bhs_logout_response) == ISCSI_BHS_SIZE); #define BHSAM_EVENT_TARGET_REQUESTS_LOGOUT 1 #define BHSAM_EVENT_TARGET_TERMINATES_CONNECTION 2 #define BHSAM_EVENT_TARGET_TERMINATES_SESSION 3 struct iscsi_bhs_asynchronous_message { uint8_t bhsam_opcode; uint8_t bhsam_flags; uint16_t bhsam_reserved; uint8_t bhsam_total_ahs_len; uint8_t bhsam_data_segment_len[3]; uint64_t bhsam_lun; uint32_t bhsam_0xffffffff; uint32_t bhsam_reserved2; uint32_t bhsam_statsn; uint32_t bhsam_expcmdsn; uint32_t bhsam_maxcmdsn; uint8_t bhsam_async_event; uint8_t bhsam_async_vcode; uint16_t bhsam_parameter1; uint16_t bhsam_parameter2; uint16_t bhsam_parameter3; uint32_t bhsam_reserved3; }; CTASSERT(sizeof(struct iscsi_bhs_asynchronous_message) == ISCSI_BHS_SIZE); #define BHSSR_REASON_DATA_DIGEST_ERROR 0x02 #define BHSSR_PROTOCOL_ERROR 0x04 #define BHSSR_COMMAND_NOT_SUPPORTED 0x05 #define BHSSR_INVALID_PDU_FIELD 0x09 struct iscsi_bhs_reject { uint8_t bhsr_opcode; uint8_t bhsr_flags; uint8_t bhsr_reason; uint8_t bhsr_reserved; uint8_t bhsr_total_ahs_len; uint8_t bhsr_data_segment_len[3]; uint64_t bhsr_reserved2; uint32_t bhsr_0xffffffff; uint32_t bhsr_reserved3; uint32_t bhsr_statsn; uint32_t bhsr_expcmdsn; uint32_t bhsr_maxcmdsn; uint32_t bhsr_datasn_r2tsn; uint32_t bhsr_reserved4; uint32_t bhsr_reserved5; }; CTASSERT(sizeof(struct iscsi_bhs_reject) == ISCSI_BHS_SIZE); #endif /* !ISCSI_PROTO_H */ Index: projects/building-blocks/sys/dev/usb/controller/xhci.c =================================================================== --- projects/building-blocks/sys/dev/usb/controller/xhci.c (revision 278195) +++ projects/building-blocks/sys/dev/usb/controller/xhci.c (revision 278196) @@ -1,4306 +1,4316 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2010 Hans Petter Selasky. 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. */ /* * USB eXtensible Host Controller Interface, a.k.a. USB 3.0 controller. * * The XHCI 1.0 spec can be found at * http://www.intel.com/technology/usb/download/xHCI_Specification_for_USB.pdf * and the USB 3.0 spec at * http://www.usb.org/developers/docs/usb_30_spec_060910.zip */ /* * A few words about the design implementation: This driver emulates * the concept about TDs which is found in EHCI specification. This * way we achieve that the USB controller drivers look similar to * eachother which makes it easier to understand the code. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR xhcidebug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #include #include #define XHCI_BUS2SC(bus) \ ((struct xhci_softc *)(((uint8_t *)(bus)) - \ ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus)))) static SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI"); static int xhcistreams; SYSCTL_INT(_hw_usb_xhci, OID_AUTO, streams, CTLFLAG_RWTUN, &xhcistreams, 0, "Set to enable streams mode support"); #ifdef USB_DEBUG static int xhcidebug; static int xhciroute; static int xhcipolling; SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RWTUN, &xhcidebug, 0, "Debug level"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, xhci_port_route, CTLFLAG_RWTUN, &xhciroute, 0, "Routing bitmap for switching EHCI ports to XHCI controller"); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RWTUN, &xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller"); #else #define xhciroute 0 #endif #define XHCI_INTR_ENDPT 1 struct xhci_std_temp { struct xhci_softc *sc; struct usb_page_cache *pc; struct xhci_td *td; struct xhci_td *td_next; uint32_t len; uint32_t offset; uint32_t max_packet_size; uint32_t average; uint16_t isoc_delta; uint16_t isoc_frame; uint8_t shortpkt; uint8_t multishort; uint8_t last_frame; uint8_t trb_type; uint8_t direction; uint8_t tbc; uint8_t tlbpc; uint8_t step_td; uint8_t do_isoc_sync; }; static void xhci_do_poll(struct usb_bus *); static void xhci_device_done(struct usb_xfer *, usb_error_t); static void xhci_root_intr(struct xhci_softc *); static void xhci_free_device_ext(struct usb_device *); static struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_device *, struct usb_endpoint_descriptor *); static usb_proc_callback_t xhci_configure_msg; static usb_error_t xhci_configure_device(struct usb_device *); static usb_error_t xhci_configure_endpoint(struct usb_device *, struct usb_endpoint_descriptor *, struct xhci_endpoint_ext *, uint16_t, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t, uint8_t); static usb_error_t xhci_configure_mask(struct usb_device *, uint32_t, uint8_t); static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *, uint64_t, uint8_t); static void xhci_endpoint_doorbell(struct usb_xfer *); static void xhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val); static uint32_t xhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr); static void xhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val); #ifdef USB_DEBUG static uint64_t xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr); #endif static const struct usb_bus_methods xhci_bus_methods; #ifdef USB_DEBUG static void xhci_dump_trb(struct xhci_trb *trb) { DPRINTFN(5, "trb = %p\n", trb); DPRINTFN(5, "qwTrb0 = 0x%016llx\n", (long long)le64toh(trb->qwTrb0)); DPRINTFN(5, "dwTrb2 = 0x%08x\n", le32toh(trb->dwTrb2)); DPRINTFN(5, "dwTrb3 = 0x%08x\n", le32toh(trb->dwTrb3)); } static void xhci_dump_endpoint(struct xhci_softc *sc, struct xhci_endp_ctx *pep) { DPRINTFN(5, "pep = %p\n", pep); DPRINTFN(5, "dwEpCtx0=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx0)); DPRINTFN(5, "dwEpCtx1=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx1)); DPRINTFN(5, "qwEpCtx2=0x%016llx\n", (long long)xhci_ctx_get_le64(sc, &pep->qwEpCtx2)); DPRINTFN(5, "dwEpCtx4=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx4)); DPRINTFN(5, "dwEpCtx5=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx5)); DPRINTFN(5, "dwEpCtx6=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx6)); DPRINTFN(5, "dwEpCtx7=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx7)); } static void xhci_dump_device(struct xhci_softc *sc, struct xhci_slot_ctx *psl) { DPRINTFN(5, "psl = %p\n", psl); DPRINTFN(5, "dwSctx0=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx0)); DPRINTFN(5, "dwSctx1=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx1)); DPRINTFN(5, "dwSctx2=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx2)); DPRINTFN(5, "dwSctx3=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx3)); } #endif uint8_t xhci_use_polling(void) { #ifdef USB_DEBUG return (xhcipolling != 0); #else return (0); #endif } static void xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) { struct xhci_softc *sc = XHCI_BUS2SC(bus); uint8_t i; cb(bus, &sc->sc_hw.root_pc, &sc->sc_hw.root_pg, sizeof(struct xhci_hw_root), XHCI_PAGE_SIZE); cb(bus, &sc->sc_hw.ctx_pc, &sc->sc_hw.ctx_pg, sizeof(struct xhci_dev_ctx_addr), XHCI_PAGE_SIZE); for (i = 0; i != XHCI_MAX_SCRATCHPADS; i++) { cb(bus, &sc->sc_hw.scratch_pc[i], &sc->sc_hw.scratch_pg[i], XHCI_PAGE_SIZE, XHCI_PAGE_SIZE); } } static void xhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset); } *ptr = htole32(val); } static uint32_t xhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset); } return (le32toh(*ptr)); } static void xhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset); } *ptr = htole64(val); } #ifdef USB_DEBUG static uint64_t xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr) { if (sc->sc_ctx_is_64_byte) { uint32_t offset; /* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */ /* all contexts are initially 32-bytes */ offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U)); ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset); } return (le64toh(*ptr)); } #endif static int xhci_reset_command_queue_locked(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; DPRINTF("\n"); temp = XREAD4(sc, oper, XHCI_CRCR_LO); if (temp & XHCI_CRCR_LO_CRR) { DPRINTF("Command ring running\n"); temp &= ~(XHCI_CRCR_LO_CS | XHCI_CRCR_LO_CA); /* * Try to abort the last command as per section * 4.6.1.2 "Aborting a Command" of the XHCI * specification: */ /* stop and cancel */ XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CS); XWRITE4(sc, oper, XHCI_CRCR_HI, 0); XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CA); XWRITE4(sc, oper, XHCI_CRCR_HI, 0); /* wait 250ms */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 4); /* check if command ring is still running */ temp = XREAD4(sc, oper, XHCI_CRCR_LO); if (temp & XHCI_CRCR_LO_CRR) { DPRINTF("Comand ring still running\n"); return (USB_ERR_IOERROR); } } /* reset command ring */ sc->sc_command_ccs = 1; sc->sc_command_idx = 0; usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); /* set up command ring control base address */ addr = buf_res.physaddr; phwr = buf_res.buffer; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0]; DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr); memset(phwr->hwr_commands, 0, sizeof(phwr->hwr_commands)); phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr); usb_pc_cpu_flush(&sc->sc_hw.root_pc); XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS); XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32)); return (0); } usb_error_t xhci_start_controller(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; struct xhci_dev_ctx_addr *pdctxa; uint64_t addr; uint32_t temp; uint16_t i; DPRINTF("\n"); sc->sc_event_ccs = 1; sc->sc_event_idx = 0; sc->sc_command_ccs = 1; sc->sc_command_idx = 0; /* Reset controller */ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_HCRST); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = (XREAD4(sc, oper, XHCI_USBCMD) & XHCI_CMD_HCRST) | (XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_CNR); if (!temp) break; } if (temp) { device_printf(sc->sc_bus.parent, "Controller " "reset timeout.\n"); return (USB_ERR_IOERROR); } if (!(XREAD4(sc, oper, XHCI_PAGESIZE) & XHCI_PAGESIZE_4K)) { device_printf(sc->sc_bus.parent, "Controller does " "not support 4K page size.\n"); return (USB_ERR_IOERROR); } temp = XREAD4(sc, capa, XHCI_HCSPARAMS1); i = XHCI_HCS1_N_PORTS(temp); if (i == 0) { device_printf(sc->sc_bus.parent, "Invalid number " "of ports: %u\n", i); return (USB_ERR_IOERROR); } sc->sc_noport = i; sc->sc_noslot = XHCI_HCS1_DEVSLOT_MAX(temp); if (sc->sc_noslot > XHCI_MAX_DEVICES) sc->sc_noslot = XHCI_MAX_DEVICES; /* set up number of device slots */ DPRINTF("CONFIG=0x%08x -> 0x%08x\n", XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot); XWRITE4(sc, oper, XHCI_CONFIG, sc->sc_noslot); DPRINTF("Max slots: %u\n", sc->sc_noslot); temp = XREAD4(sc, capa, XHCI_HCSPARAMS2); sc->sc_noscratch = XHCI_HCS2_SPB_MAX(temp); if (sc->sc_noscratch > XHCI_MAX_SCRATCHPADS) { device_printf(sc->sc_bus.parent, "XHCI request " "too many scratchpads\n"); return (USB_ERR_NOMEM); } DPRINTF("Max scratch: %u\n", sc->sc_noscratch); temp = XREAD4(sc, capa, XHCI_HCSPARAMS3); sc->sc_exit_lat_max = XHCI_HCS3_U1_DEL(temp) + XHCI_HCS3_U2_DEL(temp) + 250 /* us */; temp = XREAD4(sc, oper, XHCI_USBSTS); /* clear interrupts */ XWRITE4(sc, oper, XHCI_USBSTS, temp); /* disable all device notifications */ XWRITE4(sc, oper, XHCI_DNCTRL, 0); /* set up device context base address */ usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res); pdctxa = buf_res.buffer; memset(pdctxa, 0, sizeof(*pdctxa)); addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_dev_ctx_addr *)0)->qwSpBufPtr[0]; /* slot 0 points to the table of scratchpad pointers */ pdctxa->qwBaaDevCtxAddr[0] = htole64(addr); for (i = 0; i != sc->sc_noscratch; i++) { struct usb_page_search buf_scp; usbd_get_page(&sc->sc_hw.scratch_pc[i], 0, &buf_scp); pdctxa->qwSpBufPtr[i] = htole64((uint64_t)buf_scp.physaddr); } addr = buf_res.physaddr; XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr); XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32)); XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr); XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32)); /* Setup event table size */ temp = XREAD4(sc, capa, XHCI_HCSPARAMS2); DPRINTF("HCS2=0x%08x\n", temp); temp = XHCI_HCS2_ERST_MAX(temp); temp = 1U << temp; if (temp > XHCI_MAX_RSEG) temp = XHCI_MAX_RSEG; sc->sc_erst_max = temp; DPRINTF("ERSTSZ=0x%08x -> 0x%08x\n", XREAD4(sc, runt, XHCI_ERSTSZ(0)), temp); XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(temp)); /* Check if we should use the default IMOD value */ if (sc->sc_imod_default == 0) sc->sc_imod_default = XHCI_IMOD_DEFAULT; /* Setup interrupt rate */ XWRITE4(sc, runt, XHCI_IMOD(0), sc->sc_imod_default); usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[0]; /* reset hardware root structure */ memset(phwr, 0, sizeof(*phwr)); phwr->hwr_ring_seg[0].qwEvrsTablePtr = htole64(addr); phwr->hwr_ring_seg[0].dwEvrsTableSize = htole32(XHCI_MAX_EVENTS); DPRINTF("ERDP(0)=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32)); addr = (uint64_t)buf_res.physaddr; DPRINTF("ERSTBA(0)=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, runt, XHCI_ERSTBA_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERSTBA_HI(0), (uint32_t)(addr >> 32)); /* Setup interrupter registers */ temp = XREAD4(sc, runt, XHCI_IMAN(0)); temp |= XHCI_IMAN_INTR_ENA; XWRITE4(sc, runt, XHCI_IMAN(0), temp); /* set up command ring control base address */ addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0]; DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr); XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS); XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32)); phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr); usb_bus_mem_flush_all(&sc->sc_bus, &xhci_iterate_hw_softc); /* Go! */ XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_RS | XHCI_CMD_INTE | XHCI_CMD_HSEE); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH; if (!temp) break; } if (temp) { XWRITE4(sc, oper, XHCI_USBCMD, 0); device_printf(sc->sc_bus.parent, "Run timeout.\n"); return (USB_ERR_IOERROR); } /* catch any lost interrupts */ xhci_do_poll(&sc->sc_bus); if (sc->sc_port_route != NULL) { /* Route all ports to the XHCI by default */ sc->sc_port_route(sc->sc_bus.parent, ~xhciroute, xhciroute); } return (0); } usb_error_t xhci_halt_controller(struct xhci_softc *sc) { uint32_t temp; uint16_t i; DPRINTF("\n"); sc->sc_capa_off = 0; sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0xF; sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; /* Halt controller */ XWRITE4(sc, oper, XHCI_USBCMD, 0); for (i = 0; i != 100; i++) { usb_pause_mtx(NULL, hz / 100); temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH; if (temp) break; } if (!temp) { device_printf(sc->sc_bus.parent, "Controller halt timeout.\n"); return (USB_ERR_IOERROR); } return (0); } usb_error_t xhci_init(struct xhci_softc *sc, device_t self) { uint32_t temp; DPRINTF("\n"); /* initialize some bus fields */ sc->sc_bus.parent = self; /* set the bus revision */ sc->sc_bus.usbrev = USB_REV_3_0; /* set up the bus struct */ sc->sc_bus.methods = &xhci_bus_methods; /* set up devices array */ sc->sc_bus.devices = sc->sc_devices; sc->sc_bus.devices_max = XHCI_MAX_DEVICES; /* set default cycle state in case of early interrupts */ sc->sc_event_ccs = 1; sc->sc_command_ccs = 1; /* set up bus space offsets */ sc->sc_capa_off = 0; sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH); sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F; sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3; DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off); DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off); DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off); DPRINTF("xHCI version = 0x%04x\n", XREAD2(sc, capa, XHCI_HCIVERSION)); temp = XREAD4(sc, capa, XHCI_HCSPARAMS0); DPRINTF("HCS0 = 0x%08x\n", temp); /* set up context size */ if (XHCI_HCS0_CSZ(temp)) { sc->sc_ctx_is_64_byte = 1; } else { sc->sc_ctx_is_64_byte = 0; } /* get DMA bits */ sc->sc_bus.dma_bits = XHCI_HCS0_AC64(temp) ? 64 : 32; device_printf(self, "%d bytes context size, %d-bit DMA\n", sc->sc_ctx_is_64_byte ? 64 : 32, (int)sc->sc_bus.dma_bits); /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &xhci_iterate_hw_softc)) { return (ENOMEM); } /* set up command queue mutex and condition varible */ cv_init(&sc->sc_cmd_cv, "CMDQ"); sx_init(&sc->sc_cmd_sx, "CMDQ lock"); sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg; sc->sc_config_msg[0].bus = &sc->sc_bus; sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg; sc->sc_config_msg[1].bus = &sc->sc_bus; return (0); } void xhci_uninit(struct xhci_softc *sc) { /* * NOTE: At this point the control transfer process is gone * and "xhci_configure_msg" is no longer called. Consequently * waiting for the configuration messages to complete is not * needed. */ usb_bus_mem_free_all(&sc->sc_bus, &xhci_iterate_hw_softc); cv_destroy(&sc->sc_cmd_cv); sx_destroy(&sc->sc_cmd_sx); } static void xhci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) { struct xhci_softc *sc = XHCI_BUS2SC(bus); switch (state) { case USB_HW_POWER_SUSPEND: DPRINTF("Stopping the XHCI\n"); xhci_halt_controller(sc); break; case USB_HW_POWER_SHUTDOWN: DPRINTF("Stopping the XHCI\n"); xhci_halt_controller(sc); break; case USB_HW_POWER_RESUME: DPRINTF("Starting the XHCI\n"); xhci_start_controller(sc); break; default: break; } } static usb_error_t xhci_generic_done_sub(struct usb_xfer *xfer) { struct xhci_td *td; struct xhci_td *td_alt_next; uint32_t len; uint8_t status; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; if (xfer->aframes != xfer->nframes) usbd_xfer_set_frame_len(xfer, xfer->aframes, 0); while (1) { usb_pc_cpu_invalidate(td->page_cache); status = td->status; len = td->remainder; DPRINTFN(4, "xfer=%p[%u/%u] rem=%u/%u status=%u\n", xfer, (unsigned int)xfer->aframes, (unsigned int)xfer->nframes, (unsigned int)len, (unsigned int)td->len, (unsigned int)status); /* * Verify the status length and * add the length to "frlengths[]": */ if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status = XHCI_TRB_ERROR_LENGTH; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] += td->len - len; } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } /* Check for transfer error */ if (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED : (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS) ? USB_ERR_IOERROR : USB_ERR_NORMAL_COMPLETION); } static void xhci_generic_done(struct usb_xfer *xfer) { usb_error_t err = 0; DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) err = xhci_generic_done_sub(xfer); xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) goto done; } while (xfer->aframes != xfer->nframes) { err = xhci_generic_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) goto done; } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) err = xhci_generic_done_sub(xfer); done: /* transfer is complete */ xhci_device_done(xfer, err); } static void xhci_activate_transfer(struct usb_xfer *xfer) { struct xhci_td *td; td = xfer->td_transfer_cache; usb_pc_cpu_invalidate(td->page_cache); if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) { /* activate the transfer */ td->td_trb[0].dwTrb3 |= htole32(XHCI_TRB_3_CYCLE_BIT); usb_pc_cpu_flush(td->page_cache); xhci_endpoint_doorbell(xfer); } } static void xhci_skip_transfer(struct usb_xfer *xfer) { struct xhci_td *td; struct xhci_td *td_last; td = xfer->td_transfer_cache; td_last = xfer->td_transfer_last; td = td->alt_next; usb_pc_cpu_invalidate(td->page_cache); if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) { usb_pc_cpu_invalidate(td_last->page_cache); /* copy LINK TRB to current waiting location */ td->td_trb[0].qwTrb0 = td_last->td_trb[td_last->ntrb].qwTrb0; td->td_trb[0].dwTrb2 = td_last->td_trb[td_last->ntrb].dwTrb2; usb_pc_cpu_flush(td->page_cache); td->td_trb[0].dwTrb3 = td_last->td_trb[td_last->ntrb].dwTrb3; usb_pc_cpu_flush(td->page_cache); xhci_endpoint_doorbell(xfer); } } /*------------------------------------------------------------------------* * xhci_check_transfer *------------------------------------------------------------------------*/ static void xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb) { struct xhci_endpoint_ext *pepext; int64_t offset; uint64_t td_event; uint32_t temp; uint32_t remainder; uint16_t stream_id; uint16_t i; uint8_t status; uint8_t halted; uint8_t epno; uint8_t index; /* decode TRB */ td_event = le64toh(trb->qwTrb0); temp = le32toh(trb->dwTrb2); remainder = XHCI_TRB_2_REM_GET(temp); status = XHCI_TRB_2_ERROR_GET(temp); stream_id = XHCI_TRB_2_STREAM_GET(temp); temp = le32toh(trb->dwTrb3); epno = XHCI_TRB_3_EP_GET(temp); index = XHCI_TRB_3_SLOT_GET(temp); /* check if error means halted */ halted = (status != XHCI_TRB_ERROR_SHORT_PKT && status != XHCI_TRB_ERROR_SUCCESS); DPRINTF("slot=%u epno=%u stream=%u remainder=%u status=%u\n", index, epno, stream_id, remainder, status); if (index > sc->sc_noslot) { DPRINTF("Invalid slot.\n"); return; } if ((epno == 0) || (epno >= XHCI_MAX_ENDPOINTS)) { DPRINTF("Invalid endpoint.\n"); return; } pepext = &sc->sc_hw.devs[index].endp[epno]; if (pepext->trb_ep_mode != USB_EP_MODE_STREAMS) { stream_id = 0; DPRINTF("stream_id=0\n"); } else if (stream_id >= XHCI_MAX_STREAMS) { DPRINTF("Invalid stream ID.\n"); return; } /* try to find the USB transfer that generated the event */ for (i = 0; i != (XHCI_MAX_TRANSFERS - 1); i++) { struct usb_xfer *xfer; struct xhci_td *td; xfer = pepext->xfer[i + (XHCI_MAX_TRANSFERS * stream_id)]; if (xfer == NULL) continue; td = xfer->td_transfer_cache; DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n", (long long)td_event, (long long)td->td_self, (long long)td->td_self + sizeof(td->td_trb)); /* * NOTE: Some XHCI implementations might not trigger * an event on the last LINK TRB so we need to * consider both the last and second last event * address as conditions for a successful transfer. * * NOTE: We assume that the XHCI will only trigger one * event per chain of TRBs. */ offset = td_event - td->td_self; if (offset >= 0 && offset < (int64_t)sizeof(td->td_trb)) { usb_pc_cpu_invalidate(td->page_cache); /* compute rest of remainder, if any */ for (i = (offset / 16) + 1; i < td->ntrb; i++) { temp = le32toh(td->td_trb[i].dwTrb2); remainder += XHCI_TRB_2_BYTES_GET(temp); } DPRINTFN(5, "New remainder: %u\n", remainder); /* clear isochronous transfer errors */ if (xfer->flags_int.isochronous_xfr) { if (halted) { halted = 0; status = XHCI_TRB_ERROR_SUCCESS; remainder = td->len; } } /* "td->remainder" is verified later */ td->remainder = remainder; td->status = status; usb_pc_cpu_flush(td->page_cache); /* * 1) Last transfer descriptor makes the * transfer done */ if (((void *)td) == xfer->td_transfer_last) { DPRINTF("TD is last\n"); xhci_generic_done(xfer); break; } /* * 2) Any kind of error makes the transfer * done */ if (halted) { DPRINTF("TD has I/O error\n"); xhci_generic_done(xfer); break; } /* * 3) If there is no alternate next transfer, * a short packet also makes the transfer done */ if (td->remainder > 0) { if (td->alt_next == NULL) { DPRINTF( "short TD has no alternate next\n"); xhci_generic_done(xfer); break; } DPRINTF("TD has short pkt\n"); if (xfer->flags_int.short_frames_ok || xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr) { /* follow the alt next */ xfer->td_transfer_cache = td->alt_next; xhci_activate_transfer(xfer); break; } xhci_skip_transfer(xfer); xhci_generic_done(xfer); break; } /* * 4) Transfer complete - go to next TD */ DPRINTF("Following next TD\n"); xfer->td_transfer_cache = td->obj_next; xhci_activate_transfer(xfer); break; /* there should only be one match */ } } } static int xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb) { if (sc->sc_cmd_addr == trb->qwTrb0) { DPRINTF("Received command event\n"); sc->sc_cmd_result[0] = trb->dwTrb2; sc->sc_cmd_result[1] = trb->dwTrb3; cv_signal(&sc->sc_cmd_cv); return (1); /* command match */ } return (0); } static int xhci_interrupt_poll(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; int retval = 0; uint16_t i; uint8_t event; uint8_t j; uint8_t k; uint8_t t; usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; /* Receive any events */ usb_pc_cpu_invalidate(&sc->sc_hw.root_pc); i = sc->sc_event_idx; j = sc->sc_event_ccs; t = 2; while (1) { temp = le32toh(phwr->hwr_events[i].dwTrb3); k = (temp & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0; if (j != k) break; event = XHCI_TRB_3_TYPE_GET(temp); DPRINTFN(10, "event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0), (long)le32toh(phwr->hwr_events[i].dwTrb2), (long)le32toh(phwr->hwr_events[i].dwTrb3)); switch (event) { case XHCI_TRB_EVENT_TRANSFER: xhci_check_transfer(sc, &phwr->hwr_events[i]); break; case XHCI_TRB_EVENT_CMD_COMPLETE: retval |= xhci_check_command(sc, &phwr->hwr_events[i]); break; default: DPRINTF("Unhandled event = %u\n", event); break; } i++; if (i == XHCI_MAX_EVENTS) { i = 0; j ^= 1; /* check for timeout */ if (!--t) break; } } sc->sc_event_idx = i; sc->sc_event_ccs = j; /* * NOTE: The Event Ring Dequeue Pointer Register is 64-bit * latched. That means to activate the register we need to * write both the low and high double word of the 64-bit * register. */ addr = (uint32_t)buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i]; /* try to clear busy bit */ addr |= XHCI_ERDP_LO_BUSY; XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32)); return (retval); } static usb_error_t xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb, uint16_t timeout_ms) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; uint8_t i; uint8_t j; uint8_t timeout = 0; int err; XHCI_CMD_ASSERT_LOCKED(sc); /* get hardware root structure */ usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); phwr = buf_res.buffer; /* Queue command */ USB_BUS_LOCK(&sc->sc_bus); retry: i = sc->sc_command_idx; j = sc->sc_command_ccs; DPRINTFN(10, "command[%u] = %u (0x%016llx, 0x%08lx, 0x%08lx)\n", i, XHCI_TRB_3_TYPE_GET(le32toh(trb->dwTrb3)), (long long)le64toh(trb->qwTrb0), (long)le32toh(trb->dwTrb2), (long)le32toh(trb->dwTrb3)); phwr->hwr_commands[i].qwTrb0 = trb->qwTrb0; phwr->hwr_commands[i].dwTrb2 = trb->dwTrb2; usb_pc_cpu_flush(&sc->sc_hw.root_pc); temp = trb->dwTrb3; if (j) temp |= htole32(XHCI_TRB_3_CYCLE_BIT); else temp &= ~htole32(XHCI_TRB_3_CYCLE_BIT); temp &= ~htole32(XHCI_TRB_3_TC_BIT); phwr->hwr_commands[i].dwTrb3 = temp; usb_pc_cpu_flush(&sc->sc_hw.root_pc); addr = buf_res.physaddr; addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[i]; sc->sc_cmd_addr = htole64(addr); i++; if (i == (XHCI_MAX_COMMANDS - 1)) { if (j) { temp = htole32(XHCI_TRB_3_TC_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_CYCLE_BIT); } else { temp = htole32(XHCI_TRB_3_TC_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); } phwr->hwr_commands[i].dwTrb3 = temp; usb_pc_cpu_flush(&sc->sc_hw.root_pc); i = 0; j ^= 1; } sc->sc_command_idx = i; sc->sc_command_ccs = j; XWRITE4(sc, door, XHCI_DOORBELL(0), 0); err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(timeout_ms)); /* * In some error cases event interrupts are not generated. * Poll one time to see if the command has completed. */ if (err != 0 && xhci_interrupt_poll(sc) != 0) { DPRINTF("Command was completed when polling\n"); err = 0; } if (err != 0) { DPRINTF("Command timeout!\n"); /* * After some weeks of continuous operation, it has * been observed that the ASMedia Technology, ASM1042 * SuperSpeed USB Host Controller can suddenly stop * accepting commands via the command queue. Try to * first reset the command queue. If that fails do a * host controller reset. */ if (timeout == 0 && xhci_reset_command_queue_locked(sc) == 0) { temp = le32toh(trb->dwTrb3); /* * Avoid infinite XHCI reset loops if the set * address command fails to respond due to a * non-enumerating device: */ if (XHCI_TRB_3_TYPE_GET(temp) == XHCI_TRB_TYPE_ADDRESS_DEVICE && (temp & XHCI_TRB_3_BSR_BIT) == 0) { DPRINTF("Set address timeout\n"); } else { timeout = 1; goto retry; } } else { DPRINTF("Controller reset!\n"); usb_bus_reset_async_locked(&sc->sc_bus); } err = USB_ERR_TIMEOUT; trb->dwTrb2 = 0; trb->dwTrb3 = 0; } else { temp = le32toh(sc->sc_cmd_result[0]); if (XHCI_TRB_2_ERROR_GET(temp) != XHCI_TRB_ERROR_SUCCESS) err = USB_ERR_IOERROR; trb->dwTrb2 = sc->sc_cmd_result[0]; trb->dwTrb3 = sc->sc_cmd_result[1]; } USB_BUS_UNLOCK(&sc->sc_bus); return (err); } #if 0 static usb_error_t xhci_cmd_nop(struct xhci_softc *sc) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NOOP); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } #endif static usb_error_t xhci_cmd_enable_slot(struct xhci_softc *sc, uint8_t *pslot) { struct xhci_trb trb; uint32_t temp; usb_error_t err; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; trb.dwTrb3 = htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ENABLE_SLOT)); err = xhci_do_command(sc, &trb, 100 /* ms */); if (err) goto done; temp = le32toh(trb.dwTrb3); *pslot = XHCI_TRB_3_SLOT_GET(temp); done: return (err); } static usb_error_t xhci_cmd_disable_slot(struct xhci_softc *sc, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_set_address(struct xhci_softc *sc, uint64_t input_ctx, uint8_t bsr, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ADDRESS_DEVICE) | XHCI_TRB_3_SLOT_SET(slot_id); if (bsr) temp |= XHCI_TRB_3_BSR_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 500 /* ms */)); } static usb_error_t xhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address) { struct usb_page_search buf_inp; struct usb_page_search buf_dev; struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_hw_dev *hdev; struct xhci_dev_ctx *pdev; struct xhci_endpoint_ext *pepext; uint32_t temp; uint16_t mps; usb_error_t err; uint8_t index; /* the root HUB case is not handled here */ if (udev->parent_hub == NULL) return (USB_ERR_INVAL); index = udev->controller_slot_id; hdev = &sc->sc_hw.devs[index]; if (mtx != NULL) mtx_unlock(mtx); XHCI_CMD_LOCK(sc); switch (hdev->state) { case XHCI_ST_DEFAULT: case XHCI_ST_ENABLED: hdev->state = XHCI_ST_ENABLED; /* set configure mask to slot and EP0 */ xhci_configure_mask(udev, 3, 0); /* configure input slot context structure */ err = xhci_configure_device(udev); if (err != 0) { DPRINTF("Could not configure device\n"); break; } /* configure input endpoint context structure */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: mps = 8; break; case USB_SPEED_HIGH: mps = 64; break; default: mps = 512; break; } pepext = xhci_get_endpoint_ext(udev, &udev->ctrl_ep_desc); err = xhci_configure_endpoint(udev, &udev->ctrl_ep_desc, pepext, 0, 1, 1, 0, mps, mps, USB_EP_MODE_DEFAULT); if (err != 0) { DPRINTF("Could not configure default endpoint\n"); break; } /* execute set address command */ usbd_get_page(&hdev->input_pc, 0, &buf_inp); err = xhci_cmd_set_address(sc, buf_inp.physaddr, (address == 0), index); if (err != 0) { temp = le32toh(sc->sc_cmd_result[0]); if (address == 0 && sc->sc_port_route != NULL && XHCI_TRB_2_ERROR_GET(temp) == XHCI_TRB_ERROR_PARAMETER) { /* LynxPoint XHCI - ports are not switchable */ /* Un-route all ports from the XHCI */ sc->sc_port_route(sc->sc_bus.parent, 0, ~0); } DPRINTF("Could not set address " "for slot %u.\n", index); if (address != 0) break; } /* update device address to new value */ usbd_get_page(&hdev->device_pc, 0, &buf_dev); pdev = buf_dev.buffer; usb_pc_cpu_invalidate(&hdev->device_pc); temp = xhci_ctx_get_le32(sc, &pdev->ctx_slot.dwSctx3); udev->address = XHCI_SCTX_3_DEV_ADDR_GET(temp); /* update device state to new value */ if (address != 0) hdev->state = XHCI_ST_ADDRESSED; else hdev->state = XHCI_ST_DEFAULT; break; default: DPRINTF("Wrong state for set address.\n"); err = USB_ERR_IOERROR; break; } XHCI_CMD_UNLOCK(sc); if (mtx != NULL) mtx_lock(mtx); return (err); } static usb_error_t xhci_cmd_configure_ep(struct xhci_softc *sc, uint64_t input_ctx, uint8_t deconfigure, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP) | XHCI_TRB_3_SLOT_SET(slot_id); if (deconfigure) temp |= XHCI_TRB_3_DCEP_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *sc, uint64_t input_ctx, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(input_ctx); trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); if (preserve) temp |= XHCI_TRB_3_PRSV_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_set_tr_dequeue_ptr(struct xhci_softc *sc, uint64_t dequeue_ptr, uint16_t stream_id, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = htole64(dequeue_ptr); temp = XHCI_TRB_2_STREAM_SET(stream_id); trb.dwTrb2 = htole32(temp); temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t suspend, uint8_t ep_id, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP) | XHCI_TRB_3_SLOT_SET(slot_id) | XHCI_TRB_3_EP_SET(ep_id); if (suspend) temp |= XHCI_TRB_3_SUSP_EP_BIT; trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } static usb_error_t xhci_cmd_reset_dev(struct xhci_softc *sc, uint8_t slot_id) { struct xhci_trb trb; uint32_t temp; DPRINTF("\n"); trb.qwTrb0 = 0; trb.dwTrb2 = 0; temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_DEVICE) | XHCI_TRB_3_SLOT_SET(slot_id); trb.dwTrb3 = htole32(temp); return (xhci_do_command(sc, &trb, 100 /* ms */)); } /*------------------------------------------------------------------------* * xhci_interrupt - XHCI interrupt handler *------------------------------------------------------------------------*/ void xhci_interrupt(struct xhci_softc *sc) { uint32_t status; uint32_t temp; USB_BUS_LOCK(&sc->sc_bus); status = XREAD4(sc, oper, XHCI_USBSTS); /* acknowledge interrupts, if any */ if (status != 0) { XWRITE4(sc, oper, XHCI_USBSTS, status); DPRINTFN(16, "real interrupt (status=0x%08x)\n", status); } temp = XREAD4(sc, runt, XHCI_IMAN(0)); /* force clearing of pending interrupts */ if (temp & XHCI_IMAN_INTR_PEND) XWRITE4(sc, runt, XHCI_IMAN(0), temp); /* check for event(s) */ xhci_interrupt_poll(sc); if (status & (XHCI_STS_PCD | XHCI_STS_HCH | XHCI_STS_HSE | XHCI_STS_HCE)) { if (status & XHCI_STS_PCD) { xhci_root_intr(sc); } if (status & XHCI_STS_HCH) { printf("%s: host controller halted\n", __FUNCTION__); } if (status & XHCI_STS_HSE) { printf("%s: host system error\n", __FUNCTION__); } if (status & XHCI_STS_HCE) { printf("%s: host controller error\n", __FUNCTION__); } } USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * xhci_timeout - XHCI timeout handler *------------------------------------------------------------------------*/ static void xhci_timeout(void *arg) { struct usb_xfer *xfer = arg; DPRINTF("xfer=%p\n", xfer); USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* transfer is transferred */ xhci_device_done(xfer, USB_ERR_TIMEOUT); } static void xhci_do_poll(struct usb_bus *bus) { struct xhci_softc *sc = XHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); xhci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void xhci_setup_generic_chain_sub(struct xhci_std_temp *temp) { struct usb_page_search buf_res; struct xhci_td *td; struct xhci_td *td_next; struct xhci_td *td_alt_next; struct xhci_td *td_first; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint32_t npkt_off; uint32_t dword; uint8_t shortpkt_old; uint8_t precompute; uint8_t x; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; npkt_off = 0; precompute = 1; restart: td = temp->td; td_next = td_first = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) break; /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_packet_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) panic("%s: out of XHCI transfer descriptors!", __FUNCTION__); /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->len = average; td->remainder = 0; td->status = 0; /* update remaining length */ temp->len -= average; /* reset TRB index */ x = 0; if (temp->trb_type == XHCI_TRB_TYPE_SETUP_STAGE) { /* immediate data */ if (average > 8) average = 8; td->td_trb[0].qwTrb0 = 0; usbd_copy_out(temp->pc, temp->offset + buf_offset, (uint8_t *)(uintptr_t)&td->td_trb[0].qwTrb0, average); dword = XHCI_TRB_2_BYTES_SET(8) | XHCI_TRB_2_TDSZ_SET(0) | XHCI_TRB_2_IRQ_SET(0); td->td_trb[0].dwTrb2 = htole32(dword); dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) | XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_CYCLE_BIT; /* check wLength */ if (td->td_trb[0].qwTrb0 & htole64(XHCI_TRB_0_WLENGTH_MASK)) { if (td->td_trb[0].qwTrb0 & htole64(XHCI_TRB_0_DIR_IN_MASK)) dword |= XHCI_TRB_3_TRT_IN; else dword |= XHCI_TRB_3_TRT_OUT; } td->td_trb[0].dwTrb3 = htole32(dword); #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif x++; } else do { uint32_t npkt; /* fill out buffer pointers */ if (average == 0) { memset(&buf_res, 0, sizeof(buf_res)); } else { usbd_get_page(temp->pc, temp->offset + buf_offset, &buf_res); /* get length to end of page */ if (buf_res.length > average) buf_res.length = average; /* check for maximum length */ if (buf_res.length > XHCI_TD_PAGE_SIZE) buf_res.length = XHCI_TD_PAGE_SIZE; npkt_off += buf_res.length; } /* set up npkt */ npkt = (len_old - npkt_off + temp->max_packet_size - 1) / temp->max_packet_size; if (npkt == 0) npkt = 1; else if (npkt > 31) npkt = 31; /* fill out TRB's */ td->td_trb[x].qwTrb0 = htole64((uint64_t)buf_res.physaddr); dword = XHCI_TRB_2_BYTES_SET(buf_res.length) | XHCI_TRB_2_TDSZ_SET(npkt) | XHCI_TRB_2_IRQ_SET(0); td->td_trb[x].dwTrb2 = htole32(dword); switch (temp->trb_type) { case XHCI_TRB_TYPE_ISOCH: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TBC_SET(temp->tbc) | XHCI_TRB_3_TLBPC_SET(temp->tlbpc); if (td != td_first) { dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL); } else if (temp->do_isoc_sync != 0) { temp->do_isoc_sync = 0; /* wait until "isoc_frame" */ dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) | XHCI_TRB_3_FRID_SET(temp->isoc_frame / 8); } else { /* start data transfer at next interval */ dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) | XHCI_TRB_3_ISO_SIA_BIT; } if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_ISP_BIT; break; case XHCI_TRB_TYPE_DATA_STAGE: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT; + /* + * Section 3.2.9 in the XHCI + * specification about control + * transfers says that we should use a + * normal-TRB if there are more TRBs + * extending the data-stage + * TRB. Update the "trb_type". + */ + temp->trb_type = XHCI_TRB_TYPE_NORMAL; break; case XHCI_TRB_TYPE_STATUS_STAGE: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_DIR_IN; break; default: /* XHCI_TRB_TYPE_NORMAL */ dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_ISP_BIT; break; } td->td_trb[x].dwTrb3 = htole32(dword); average -= buf_res.length; buf_offset += buf_res.length; #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif x++; } while (average != 0); td->td_trb[x-1].dwTrb3 |= htole32(XHCI_TRB_3_IOC_BIT); /* store number of data TRB's */ td->ntrb = x; DPRINTF("NTRB=%u\n", x); /* fill out link TRB */ if (td_next != NULL) { /* link the current TD with the next one */ td->td_trb[x].qwTrb0 = htole64((uint64_t)td_next->td_self); DPRINTF("LINK=0x%08llx\n", (long long)td_next->td_self); } else { /* this field will get updated later */ DPRINTF("NOLINK\n"); } dword = XHCI_TRB_2_IRQ_SET(0); td->td_trb[x].dwTrb2 = htole32(dword); dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_IOC_BIT | /* * CHAIN-BIT: Ensure that a multi-TRB IN-endpoint * frame only receives a single short packet event * by setting the CHAIN bit in the LINK field. In * addition some XHCI controllers have problems * sending a ZLP unless the CHAIN-BIT is set in * the LINK TRB. */ XHCI_TRB_3_CHAIN_BIT; td->td_trb[x].dwTrb3 = htole32(dword); td->alt_next = td_alt_next; #ifdef USB_DEBUG xhci_dump_trb(&td->td_trb[x]); #endif usb_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* set up alt next pointer, if any */ if (temp->last_frame) { td_alt_next = NULL; } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } /* * Remove cycle bit from the first TRB if we are * stepping them: */ if (temp->step_td != 0) { td_first->td_trb[0].dwTrb3 &= ~htole32(XHCI_TRB_3_CYCLE_BIT); usb_pc_cpu_flush(td_first->page_cache); } /* clear TD SIZE to zero, hence this is the last TRB */ /* remove chain bit because this is the last data TRB in the chain */ td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); /* remove CHAIN-BIT from last LINK TRB */ td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); usb_pc_cpu_flush(td->page_cache); temp->td = td; temp->td_next = td_next; } static void xhci_setup_generic_chain(struct usb_xfer *xfer) { struct xhci_std_temp temp; struct xhci_td *td; uint32_t x; uint32_t y; uint8_t mult; temp.do_isoc_sync = 0; temp.step_td = 0; temp.tbc = 0; temp.tlbpc = 0; temp.average = xfer->max_hc_frame_size; temp.max_packet_size = xfer->max_packet_size; temp.sc = XHCI_BUS2SC(xfer->xroot->bus); temp.pc = NULL; temp.last_frame = 0; temp.offset = 0; temp.multishort = xfer->flags_int.isochronous_xfr || xfer->flags_int.control_xfr || xfer->flags_int.short_frames_ok; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; temp.td = NULL; temp.td_next = td; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; if (xfer->flags_int.isochronous_xfr) { uint8_t shift; /* compute multiplier for ISOCHRONOUS transfers */ mult = xfer->endpoint->ecomp ? UE_GET_SS_ISO_MULT(xfer->endpoint->ecomp->bmAttributes) : 0; /* check for USB 2.0 multiplier */ if (mult == 0) { mult = (xfer->endpoint->edesc-> wMaxPacketSize[1] >> 3) & 3; } /* range check */ if (mult > 2) mult = 3; else mult++; x = XREAD4(temp.sc, runt, XHCI_MFINDEX); DPRINTF("MFINDEX=0x%08x\n", x); switch (usbd_get_speed(xfer->xroot->udev)) { case USB_SPEED_FULL: shift = 3; temp.isoc_delta = 8; /* 1ms */ x += temp.isoc_delta - 1; x &= ~(temp.isoc_delta - 1); break; default: shift = usbd_xfer_get_fps_shift(xfer); temp.isoc_delta = 1U << shift; x += temp.isoc_delta - 1; x &= ~(temp.isoc_delta - 1); /* simple frame load balancing */ x += xfer->endpoint->usb_uframe; break; } y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next); if ((xfer->endpoint->is_synced == 0) || (y < (xfer->nframes << shift)) || (XHCI_MFINDEX_GET(-y) >= (128 * 8))) { /* * If there is data underflow or the pipe * queue is empty we schedule the transfer a * few frames ahead of the current frame * position. Else two isochronous transfers * might overlap. */ xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8)); xfer->endpoint->is_synced = 1; temp.do_isoc_sync = 1; DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next); } /* compute isochronous completion time */ y = XHCI_MFINDEX_GET(xfer->endpoint->isoc_next - (x & ~7)); xfer->isoc_time_complete = usb_isoc_time_expand(&temp.sc->sc_bus, x / 8) + (y / 8) + (((xfer->nframes << shift) + 7) / 8); x = 0; temp.isoc_frame = xfer->endpoint->isoc_next; temp.trb_type = XHCI_TRB_TYPE_ISOCH; xfer->endpoint->isoc_next += xfer->nframes << shift; } else if (xfer->flags_int.control_xfr) { /* check if we should prepend a setup message */ if (xfer->flags_int.control_hdr) { temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; temp.trb_type = XHCI_TRB_TYPE_SETUP_STAGE; temp.direction = 0; /* check for last frame */ if (xfer->nframes == 1) { /* no STATUS stage yet, SETUP is last */ if (xfer->flags_int.control_act) temp.last_frame = 1; } xhci_setup_generic_chain_sub(&temp); } x = 1; mult = 1; temp.isoc_delta = 0; temp.isoc_frame = 0; - temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE; + temp.trb_type = xfer->flags_int.control_did_data ? + XHCI_TRB_TYPE_NORMAL : XHCI_TRB_TYPE_DATA_STAGE; } else { x = 0; mult = 1; temp.isoc_delta = 0; temp.isoc_frame = 0; temp.trb_type = XHCI_TRB_TYPE_NORMAL; } if (x != xfer->nframes) { /* set up page_cache pointer */ temp.pc = xfer->frbuffers + x; /* set endpoint direction */ temp.direction = UE_GET_DIR(xfer->endpointno); } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.step_td = ((xfer->endpointno & UE_DIR_IN) && x != 0 && temp.multishort == 0); x++; if (x == xfer->nframes) { if (xfer->flags_int.control_xfr) { /* no STATUS stage yet, DATA is last */ if (xfer->flags_int.control_act) temp.last_frame = 1; } else { temp.last_frame = 1; } } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; temp.tbc = 0; temp.tlbpc = mult - 1; } else if (xfer->flags_int.isochronous_xfr) { uint8_t tdpc; /* * Isochronous transfers don't have short * packet termination: */ temp.shortpkt = 1; /* isochronous transfers have a transfer limit */ if (temp.len > xfer->max_frame_size) temp.len = xfer->max_frame_size; /* compute TD packet count */ tdpc = (temp.len + xfer->max_packet_size - 1) / xfer->max_packet_size; temp.tbc = ((tdpc + mult - 1) / mult) - 1; temp.tlbpc = (tdpc % mult); if (temp.tlbpc == 0) temp.tlbpc = mult - 1; else temp.tlbpc--; } else { /* regular data transfer */ temp.shortpkt = xfer->flags.force_short_xfer ? 0 : 1; } xhci_setup_generic_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += xfer->frlengths[x - 1]; temp.isoc_frame += temp.isoc_delta; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ temp.step_td = (xfer->nframes != 0); temp.direction = UE_GET_DIR(xfer->endpointno) ^ UE_DIR_IN; temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; temp.last_frame = 1; temp.trb_type = XHCI_TRB_TYPE_STATUS_STAGE; xhci_setup_generic_chain_sub(&temp); } td = temp.td; /* must have at least one frame! */ xfer->td_transfer_last = td; DPRINTF("first=%p last=%p\n", xfer->td_transfer_first, td); } static void xhci_set_slot_pointer(struct xhci_softc *sc, uint8_t index, uint64_t dev_addr) { struct usb_page_search buf_res; struct xhci_dev_ctx_addr *pdctxa; usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res); pdctxa = buf_res.buffer; DPRINTF("addr[%u]=0x%016llx\n", index, (long long)dev_addr); pdctxa->qwBaaDevCtxAddr[index] = htole64(dev_addr); usb_pc_cpu_flush(&sc->sc_hw.ctx_pc); } static usb_error_t xhci_configure_mask(struct usb_device *udev, uint32_t mask, uint8_t drop) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; struct xhci_input_dev_ctx *pinp; uint32_t temp; uint8_t index; uint8_t x; index = udev->controller_slot_id; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); pinp = buf_inp.buffer; if (drop) { mask &= XHCI_INCTX_NON_CTRL_MASK; xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0, mask); xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, 0); } else { /* * Some hardware requires that we drop the endpoint * context before adding it again: */ xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0, mask & XHCI_INCTX_NON_CTRL_MASK); /* Add new endpoint context */ xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, mask); /* find most significant set bit */ for (x = 31; x != 1; x--) { if (mask & (1 << x)) break; } /* adjust */ x--; /* figure out the maximum number of contexts */ if (x > sc->sc_hw.devs[index].context_num) sc->sc_hw.devs[index].context_num = x; else x = sc->sc_hw.devs[index].context_num; /* update number of contexts */ temp = xhci_ctx_get_le32(sc, &pinp->ctx_slot.dwSctx0); temp &= ~XHCI_SCTX_0_CTX_NUM_SET(31); temp |= XHCI_SCTX_0_CTX_NUM_SET(x + 1); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp); } usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc); return (0); } static usb_error_t xhci_configure_endpoint(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct xhci_endpoint_ext *pepext, uint16_t interval, uint8_t max_packet_count, uint8_t mult, uint8_t fps_shift, uint16_t max_packet_size, uint16_t max_frame_size, uint8_t ep_mode) { struct usb_page_search buf_inp; struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_input_dev_ctx *pinp; uint64_t ring_addr = pepext->physaddr; uint32_t temp; uint8_t index; uint8_t epno; uint8_t type; index = udev->controller_slot_id; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); pinp = buf_inp.buffer; epno = edesc->bEndpointAddress; type = edesc->bmAttributes & UE_XFERTYPE; if (type == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); if (epno == 0) return (USB_ERR_NO_PIPE); /* invalid */ if (max_packet_count == 0) return (USB_ERR_BAD_BUFSIZE); max_packet_count--; if (mult == 0) return (USB_ERR_BAD_BUFSIZE); /* store endpoint mode */ pepext->trb_ep_mode = ep_mode; usb_pc_cpu_flush(pepext->page_cache); if (ep_mode == USB_EP_MODE_STREAMS) { temp = XHCI_EPCTX_0_EPSTATE_SET(0) | XHCI_EPCTX_0_MAXP_STREAMS_SET(XHCI_MAX_STREAMS_LOG - 1) | XHCI_EPCTX_0_LSA_SET(1); ring_addr += sizeof(struct xhci_trb) * XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS; } else { temp = XHCI_EPCTX_0_EPSTATE_SET(0) | XHCI_EPCTX_0_MAXP_STREAMS_SET(0) | XHCI_EPCTX_0_LSA_SET(0); ring_addr |= XHCI_EPCTX_2_DCS_SET(1); } switch (udev->speed) { case USB_SPEED_FULL: case USB_SPEED_LOW: /* 1ms -> 125us */ fps_shift += 3; break; default: break; } switch (type) { case UE_INTERRUPT: if (fps_shift > 3) fps_shift--; temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift); break; case UE_ISOCHRONOUS: temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift); switch (udev->speed) { case USB_SPEED_SUPER: if (mult > 3) mult = 3; temp |= XHCI_EPCTX_0_MULT_SET(mult - 1); max_packet_count /= mult; break; default: break; } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx0, temp); temp = XHCI_EPCTX_1_HID_SET(0) | XHCI_EPCTX_1_MAXB_SET(max_packet_count) | XHCI_EPCTX_1_MAXP_SIZE_SET(max_packet_size); /* * Always enable the "three strikes and you are gone" feature * except for ISOCHRONOUS endpoints. This is suggested by * section 4.3.3 in the XHCI specification about device slot * initialisation. */ if (type != UE_ISOCHRONOUS) temp |= XHCI_EPCTX_1_CERR_SET(3); switch (type) { case UE_CONTROL: temp |= XHCI_EPCTX_1_EPTYPE_SET(4); break; case UE_ISOCHRONOUS: temp |= XHCI_EPCTX_1_EPTYPE_SET(1); break; case UE_BULK: temp |= XHCI_EPCTX_1_EPTYPE_SET(2); break; default: temp |= XHCI_EPCTX_1_EPTYPE_SET(3); break; } /* check for IN direction */ if (epno & 1) temp |= XHCI_EPCTX_1_EPTYPE_SET(4); xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx1, temp); xhci_ctx_set_le64(sc, &pinp->ctx_ep[epno - 1].qwEpCtx2, ring_addr); switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_ISOCHRONOUS: temp = XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size) | XHCI_EPCTX_4_AVG_TRB_LEN_SET(MIN(XHCI_PAGE_SIZE, max_frame_size)); break; case UE_CONTROL: temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(8); break; default: temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_PAGE_SIZE); break; } xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx4, temp); #ifdef USB_DEBUG xhci_dump_endpoint(sc, &pinp->ctx_ep[epno - 1]); #endif usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc); return (0); /* success */ } static usb_error_t xhci_configure_endpoint_by_xfer(struct usb_xfer *xfer) { struct xhci_endpoint_ext *pepext; struct usb_endpoint_ss_comp_descriptor *ecomp; usb_stream_t x; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); ecomp = xfer->endpoint->ecomp; for (x = 0; x != XHCI_MAX_STREAMS; x++) { uint64_t temp; /* halt any transfers */ pepext->trb[x * XHCI_MAX_TRANSFERS].dwTrb3 = 0; /* compute start of TRB ring for stream "x" */ temp = pepext->physaddr + (x * XHCI_MAX_TRANSFERS * sizeof(struct xhci_trb)) + XHCI_SCTX_0_SCT_SEC_TR_RING; /* make tree structure */ pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].qwTrb0 = htole64(temp); /* reserved fields */ pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].dwTrb2 = 0; pepext->trb[(XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS) + x].dwTrb3 = 0; } usb_pc_cpu_flush(pepext->page_cache); return (xhci_configure_endpoint(xfer->xroot->udev, xfer->endpoint->edesc, pepext, xfer->interval, xfer->max_packet_count, (ecomp != NULL) ? UE_GET_SS_ISO_MULT(ecomp->bmAttributes) + 1 : 1, usbd_xfer_get_fps_shift(xfer), xfer->max_packet_size, xfer->max_frame_size, xfer->endpoint->ep_mode)); } static usb_error_t xhci_configure_device(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; struct usb_page_cache *pcinp; struct xhci_input_dev_ctx *pinp; struct usb_device *hubdev; uint32_t temp; uint32_t route; uint32_t rh_port; uint8_t is_hub; uint8_t index; uint8_t depth; index = udev->controller_slot_id; DPRINTF("index=%u\n", index); pcinp = &sc->sc_hw.devs[index].input_pc; usbd_get_page(pcinp, 0, &buf_inp); pinp = buf_inp.buffer; rh_port = 0; route = 0; /* figure out route string and root HUB port number */ for (hubdev = udev; hubdev != NULL; hubdev = hubdev->parent_hub) { if (hubdev->parent_hub == NULL) break; depth = hubdev->parent_hub->depth; /* * NOTE: HS/FS/LS devices and the SS root HUB can have * more than 15 ports */ rh_port = hubdev->port_no; if (depth == 0) break; if (rh_port > 15) rh_port = 15; if (depth < 6) route |= rh_port << (4 * (depth - 1)); } DPRINTF("Route=0x%08x\n", route); temp = XHCI_SCTX_0_ROUTE_SET(route) | XHCI_SCTX_0_CTX_NUM_SET( sc->sc_hw.devs[index].context_num + 1); switch (udev->speed) { case USB_SPEED_LOW: temp |= XHCI_SCTX_0_SPEED_SET(2); if (udev->parent_hs_hub != NULL && udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("Device inherits MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; case USB_SPEED_HIGH: temp |= XHCI_SCTX_0_SPEED_SET(3); if (sc->sc_hw.devs[index].nports != 0 && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("HUB supports MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; case USB_SPEED_FULL: temp |= XHCI_SCTX_0_SPEED_SET(1); if (udev->parent_hs_hub != NULL && udev->parent_hs_hub->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) { DPRINTF("Device inherits MTT\n"); temp |= XHCI_SCTX_0_MTT_SET(1); } break; default: temp |= XHCI_SCTX_0_SPEED_SET(4); break; } is_hub = sc->sc_hw.devs[index].nports != 0 && (udev->speed == USB_SPEED_SUPER || udev->speed == USB_SPEED_HIGH); if (is_hub) temp |= XHCI_SCTX_0_HUB_SET(1); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp); temp = XHCI_SCTX_1_RH_PORT_SET(rh_port); if (is_hub) { temp |= XHCI_SCTX_1_NUM_PORTS_SET( sc->sc_hw.devs[index].nports); } switch (udev->speed) { case USB_SPEED_SUPER: switch (sc->sc_hw.devs[index].state) { case XHCI_ST_ADDRESSED: case XHCI_ST_CONFIGURED: /* enable power save */ temp |= XHCI_SCTX_1_MAX_EL_SET(sc->sc_exit_lat_max); break; default: /* disable power save */ break; } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx1, temp); temp = XHCI_SCTX_2_IRQ_TARGET_SET(0); if (is_hub) { temp |= XHCI_SCTX_2_TT_THINK_TIME_SET( sc->sc_hw.devs[index].tt); } hubdev = udev->parent_hs_hub; /* check if we should activate the transaction translator */ switch (udev->speed) { case USB_SPEED_FULL: case USB_SPEED_LOW: if (hubdev != NULL) { temp |= XHCI_SCTX_2_TT_HUB_SID_SET( hubdev->controller_slot_id); temp |= XHCI_SCTX_2_TT_PORT_NUM_SET( udev->hs_port_no); } break; default: break; } xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx2, temp); /* * These fields should be initialized to zero, according to * XHCI section 6.2.2 - slot context: */ temp = XHCI_SCTX_3_DEV_ADDR_SET(0) | XHCI_SCTX_3_SLOT_STATE_SET(0); xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx3, temp); #ifdef USB_DEBUG xhci_dump_device(sc, &pinp->ctx_slot); #endif usb_pc_cpu_flush(pcinp); return (0); /* success */ } static usb_error_t xhci_alloc_device_ext(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_dev; struct usb_page_search buf_ep; struct xhci_trb *trb; struct usb_page_cache *pc; struct usb_page *pg; uint64_t addr; uint8_t index; uint8_t i; index = udev->controller_slot_id; pc = &sc->sc_hw.devs[index].device_pc; pg = &sc->sc_hw.devs[index].device_pg; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ? (2 * sizeof(struct xhci_dev_ctx)) : sizeof(struct xhci_dev_ctx), XHCI_PAGE_SIZE)) goto error; usbd_get_page(pc, 0, &buf_dev); pc = &sc->sc_hw.devs[index].input_pc; pg = &sc->sc_hw.devs[index].input_pg; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ? (2 * sizeof(struct xhci_input_dev_ctx)) : sizeof(struct xhci_input_dev_ctx), XHCI_PAGE_SIZE)) { goto error; } /* initialize all endpoint LINK TRBs */ for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) { pc = &sc->sc_hw.devs[index].endpoint_pc[i]; pg = &sc->sc_hw.devs[index].endpoint_pg[i]; /* need to initialize the page cache */ pc->tag_parent = sc->sc_bus.dma_parent_tag; if (usb_pc_alloc_mem(pc, pg, sizeof(struct xhci_dev_endpoint_trbs), XHCI_TRB_ALIGN)) { goto error; } /* lookup endpoint TRB ring */ usbd_get_page(pc, 0, &buf_ep); /* get TRB pointer */ trb = buf_ep.buffer; trb += XHCI_MAX_TRANSFERS - 1; /* get TRB start address */ addr = buf_ep.physaddr; /* create LINK TRB */ trb->qwTrb0 = htole64(addr); trb->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); trb->dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); usb_pc_cpu_flush(pc); } xhci_set_slot_pointer(sc, index, buf_dev.physaddr); return (0); error: xhci_free_device_ext(udev); return (USB_ERR_NOMEM); } static void xhci_free_device_ext(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t i; index = udev->controller_slot_id; xhci_set_slot_pointer(sc, index, 0); usb_pc_free_mem(&sc->sc_hw.devs[index].device_pc); usb_pc_free_mem(&sc->sc_hw.devs[index].input_pc); for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) usb_pc_free_mem(&sc->sc_hw.devs[index].endpoint_pc[i]); } static struct xhci_endpoint_ext * xhci_get_endpoint_ext(struct usb_device *udev, struct usb_endpoint_descriptor *edesc) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct xhci_endpoint_ext *pepext; struct usb_page_cache *pc; struct usb_page_search buf_ep; uint8_t epno; uint8_t index; epno = edesc->bEndpointAddress; if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); index = udev->controller_slot_id; pc = &sc->sc_hw.devs[index].endpoint_pc[epno]; usbd_get_page(pc, 0, &buf_ep); pepext = &sc->sc_hw.devs[index].endp[epno]; pepext->page_cache = pc; pepext->trb = buf_ep.buffer; pepext->physaddr = buf_ep.physaddr; return (pepext); } static void xhci_endpoint_doorbell(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); uint8_t epno; uint8_t index; epno = xfer->endpointno; if (xfer->flags_int.control_xfr) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); index = xfer->xroot->udev->controller_slot_id; if (xfer->xroot->udev->flags.self_suspended == 0) { XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(xfer->stream_id)); } } static void xhci_transfer_remove(struct usb_xfer *xfer, usb_error_t error) { struct xhci_endpoint_ext *pepext; if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); pepext->trb_used[xfer->stream_id]--; pepext->xfer[xfer->qh_pos] = NULL; if (error && pepext->trb_running != 0) { pepext->trb_halted = 1; pepext->trb_running = 0; } } } static usb_error_t xhci_transfer_insert(struct usb_xfer *xfer) { struct xhci_td *td_first; struct xhci_td *td_last; struct xhci_trb *trb_link; struct xhci_endpoint_ext *pepext; uint64_t addr; usb_stream_t id; uint8_t i; uint8_t inext; uint8_t trb_limit; DPRINTFN(8, "\n"); id = xfer->stream_id; /* check if already inserted */ if (xfer->flags_int.bandwidth_reclaimed) { DPRINTFN(8, "Already in schedule\n"); return (0); } pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); td_first = xfer->td_transfer_first; td_last = xfer->td_transfer_last; addr = pepext->physaddr; switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: case UE_INTERRUPT: /* single buffered */ trb_limit = 1; break; default: /* multi buffered */ trb_limit = (XHCI_MAX_TRANSFERS - 2); break; } if (pepext->trb_used[id] >= trb_limit) { DPRINTFN(8, "Too many TDs queued.\n"); return (USB_ERR_NOMEM); } /* check for stopped condition, after putting transfer on interrupt queue */ if (pepext->trb_running == 0) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); DPRINTFN(8, "Not running\n"); /* start configuration */ (void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus), &sc->sc_config_msg[0], &sc->sc_config_msg[1]); return (0); } pepext->trb_used[id]++; /* get current TRB index */ i = pepext->trb_index[id]; /* get next TRB index */ inext = (i + 1); /* the last entry of the ring is a hardcoded link TRB */ if (inext >= (XHCI_MAX_TRANSFERS - 1)) inext = 0; /* store next TRB index, before stream ID offset is added */ pepext->trb_index[id] = inext; /* offset for stream */ i += id * XHCI_MAX_TRANSFERS; inext += id * XHCI_MAX_TRANSFERS; /* compute terminating return address */ addr += (inext * sizeof(struct xhci_trb)); /* compute link TRB pointer */ trb_link = td_last->td_trb + td_last->ntrb; /* update next pointer of last link TRB */ trb_link->qwTrb0 = htole64(addr); trb_link->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); trb_link->dwTrb3 = htole32(XHCI_TRB_3_IOC_BIT | XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); #ifdef USB_DEBUG xhci_dump_trb(&td_last->td_trb[td_last->ntrb]); #endif usb_pc_cpu_flush(td_last->page_cache); /* write ahead chain end marker */ pepext->trb[inext].qwTrb0 = 0; pepext->trb[inext].dwTrb2 = 0; pepext->trb[inext].dwTrb3 = 0; /* update next pointer of link TRB */ pepext->trb[i].qwTrb0 = htole64((uint64_t)td_first->td_self); pepext->trb[i].dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); #ifdef USB_DEBUG xhci_dump_trb(&pepext->trb[i]); #endif usb_pc_cpu_flush(pepext->page_cache); /* toggle cycle bit which activates the transfer chain */ pepext->trb[i].dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); usb_pc_cpu_flush(pepext->page_cache); DPRINTF("qh_pos = %u\n", i); pepext->xfer[i] = xfer; xfer->qh_pos = i; xfer->flags_int.bandwidth_reclaimed = 1; xhci_endpoint_doorbell(xfer); return (0); } static void xhci_root_intr(struct xhci_softc *sc) { uint16_t i; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* clear any old interrupt data */ memset(sc->sc_hub_idata, 0, sizeof(sc->sc_hub_idata)); for (i = 1; i <= sc->sc_noport; i++) { /* pick out CHANGE bits from the status register */ if (XREAD4(sc, oper, XHCI_PORTSC(i)) & ( XHCI_PS_CSC | XHCI_PS_PEC | XHCI_PS_OCC | XHCI_PS_WRC | XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC)) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); } /*------------------------------------------------------------------------* * xhci_device_done - XHCI done handler * * NOTE: This function can be called two times in a row on * the same USB transfer. From close and from interrupt. *------------------------------------------------------------------------*/ static void xhci_device_done(struct usb_xfer *xfer, usb_error_t error) { DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); /* remove transfer from HW queue */ xhci_transfer_remove(xfer, error); /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * XHCI data transfer support (generic type) *------------------------------------------------------------------------*/ static void xhci_device_generic_open(struct usb_xfer *xfer) { if (xfer->flags_int.isochronous_xfr) { switch (xfer->xroot->udev->speed) { case USB_SPEED_FULL: break; default: usb_hs_bandwidth_alloc(xfer); break; } } } static void xhci_device_generic_close(struct usb_xfer *xfer) { DPRINTF("\n"); xhci_device_done(xfer, USB_ERR_CANCELLED); if (xfer->flags_int.isochronous_xfr) { switch (xfer->xroot->udev->speed) { case USB_SPEED_FULL: break; default: usb_hs_bandwidth_free(xfer); break; } } } static void xhci_device_generic_multi_enter(struct usb_endpoint *ep, usb_stream_t stream_id, struct usb_xfer *enter_xfer) { struct usb_xfer *xfer; /* check if there is a current transfer */ xfer = ep->endpoint_q[stream_id].curr; if (xfer == NULL) return; /* * Check if the current transfer is started and then pickup * the next one, if any. Else wait for next start event due to * block on failure feature. */ if (!xfer->flags_int.bandwidth_reclaimed) return; xfer = TAILQ_FIRST(&ep->endpoint_q[stream_id].head); if (xfer == NULL) { /* * In case of enter we have to consider that the * transfer is queued by the USB core after the enter * method is called. */ xfer = enter_xfer; if (xfer == NULL) return; } /* try to multi buffer */ xhci_transfer_insert(xfer); } static void xhci_device_generic_enter(struct usb_xfer *xfer) { DPRINTF("\n"); /* set up TD's and QH */ xhci_setup_generic_chain(xfer); xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, xfer); } static void xhci_device_generic_start(struct usb_xfer *xfer) { DPRINTF("\n"); /* try to insert xfer on HW queue */ xhci_transfer_insert(xfer); /* try to multi buffer */ xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, NULL); /* add transfer last on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout); } static const struct usb_pipe_methods xhci_device_generic_methods = { .open = xhci_device_generic_open, .close = xhci_device_generic_close, .enter = xhci_device_generic_enter, .start = xhci_device_generic_start, }; /*------------------------------------------------------------------------* * xhci root HUB support *------------------------------------------------------------------------* * Simulate a hardware HUB by handling all the necessary requests. *------------------------------------------------------------------------*/ #define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) } static const struct usb_device_descriptor xhci_devd = { .bLength = sizeof(xhci_devd), .bDescriptorType = UDESC_DEVICE, /* type */ HSETW(.bcdUSB, 0x0300), /* USB version */ .bDeviceClass = UDCLASS_HUB, /* class */ .bDeviceSubClass = UDSUBCLASS_HUB, /* subclass */ .bDeviceProtocol = UDPROTO_SSHUB, /* protocol */ .bMaxPacketSize = 9, /* max packet size */ HSETW(.idVendor, 0x0000), /* vendor */ HSETW(.idProduct, 0x0000), /* product */ HSETW(.bcdDevice, 0x0100), /* device version */ .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 0, .bNumConfigurations = 1, /* # of configurations */ }; static const struct xhci_bos_desc xhci_bosd = { .bosd = { .bLength = sizeof(xhci_bosd.bosd), .bDescriptorType = UDESC_BOS, HSETW(.wTotalLength, sizeof(xhci_bosd)), .bNumDeviceCaps = 3, }, .usb2extd = { .bLength = sizeof(xhci_bosd.usb2extd), .bDescriptorType = 1, .bDevCapabilityType = 2, .bmAttributes[0] = 2, }, .usbdcd = { .bLength = sizeof(xhci_bosd.usbdcd), .bDescriptorType = UDESC_DEVICE_CAPABILITY, .bDevCapabilityType = 3, .bmAttributes = 0, /* XXX */ HSETW(.wSpeedsSupported, 0x000C), .bFunctionalitySupport = 8, .bU1DevExitLat = 255, /* dummy - not used */ .wU2DevExitLat = { 0x00, 0x08 }, }, .cidd = { .bLength = sizeof(xhci_bosd.cidd), .bDescriptorType = 1, .bDevCapabilityType = 4, .bReserved = 0, .bContainerID = 0, /* XXX */ }, }; static const struct xhci_config_desc xhci_confd = { .confd = { .bLength = sizeof(xhci_confd.confd), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(xhci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(xhci_confd.ifcd), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = 0, }, .endpd = { .bLength = sizeof(xhci_confd.endpd), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | XHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 2, /* max 15 ports */ .bInterval = 255, }, .endpcd = { .bLength = sizeof(xhci_confd.endpcd), .bDescriptorType = UDESC_ENDPOINT_SS_COMP, .bMaxBurst = 0, .bmAttributes = 0, }, }; static const struct usb_hub_ss_descriptor xhci_hubd = { .bLength = sizeof(xhci_hubd), .bDescriptorType = UDESC_SS_HUB, }; static usb_error_t xhci_roothub_exec(struct usb_device *udev, struct usb_device_request *req, const void **pptr, uint16_t *plength) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); const char *str_ptr; const void *ptr; uint32_t port; uint32_t v; uint16_t len; uint16_t i; uint16_t value; uint16_t index; uint8_t j; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); /* buffer reset */ ptr = (const void *)&sc->sc_hub_desc; len = 0; err = 0; value = UGETW(req->wValue); index = UGETW(req->wIndex); DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", req->bmRequestType, req->bRequest, UGETW(req->wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_devd); ptr = (const void *)&xhci_devd; break; case UDESC_BOS: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_bosd); ptr = (const void *)&xhci_bosd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } len = sizeof(xhci_confd); ptr = (const void *)&xhci_confd; break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ str_ptr = "\001"; break; case 1: /* Vendor */ str_ptr = sc->sc_vendor; break; case 2: /* Product */ str_ptr = "XHCI root HUB"; break; default: str_ptr = ""; break; } len = usb_make_str_desc( sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), str_ptr); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= XHCI_MAX_DEVICES) { err = USB_ERR_IOERROR; goto done; } break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if (value != 0 && value != 1) { err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n"); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTSC(index); v = XREAD4(sc, oper, port); i = XHCI_PS_PLS_GET(v); v &= ~XHCI_PS_CLEAR; switch (value) { case UHF_C_BH_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_WRC); break; case UHF_C_PORT_CONFIG_ERROR: XWRITE4(sc, oper, port, v | XHCI_PS_CEC); break; case UHF_C_PORT_SUSPEND: case UHF_C_PORT_LINK_STATE: XWRITE4(sc, oper, port, v | XHCI_PS_PLC); break; case UHF_C_PORT_CONNECTION: XWRITE4(sc, oper, port, v | XHCI_PS_CSC); break; case UHF_C_PORT_ENABLE: XWRITE4(sc, oper, port, v | XHCI_PS_PEC); break; case UHF_C_PORT_OVER_CURRENT: XWRITE4(sc, oper, port, v | XHCI_PS_OCC); break; case UHF_C_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_PRC); break; case UHF_PORT_ENABLE: XWRITE4(sc, oper, port, v | XHCI_PS_PED); break; case UHF_PORT_POWER: XWRITE4(sc, oper, port, v & ~XHCI_PS_PP); break; case UHF_PORT_INDICATOR: XWRITE4(sc, oper, port, v & ~XHCI_PS_PIC_SET(3)); break; case UHF_PORT_SUSPEND: /* U3 -> U15 */ if (i == 3) { XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(0xF) | XHCI_PS_LWS); } /* wait 20ms for resume sequence to complete */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); /* U0 */ XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(0) | XHCI_PS_LWS); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { err = USB_ERR_IOERROR; goto done; } v = XREAD4(sc, capa, XHCI_HCSPARAMS0); sc->sc_hub_desc.hubd = xhci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; if (XHCI_HCS0_PPC(v)) i = UHD_PWR_INDIVIDUAL; else i = UHD_PWR_GANGED; if (XHCI_HCS0_PIND(v)) i |= UHD_PORT_IND; i |= UHD_OC_INDIVIDUAL; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i); /* see XHCI section 5.4.9: */ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 10; for (j = 1; j <= sc->sc_noport; j++) { v = XREAD4(sc, oper, XHCI_PORTSC(j)); if (v & XHCI_PS_DR) { sc->sc_hub_desc.hubd. DeviceRemovable[j / 8] |= 1U << (j % 8); } } len = sc->sc_hub_desc.hubd.bLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "UR_GET_STATUS i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } v = XREAD4(sc, oper, XHCI_PORTSC(index)); DPRINTFN(9, "port status=0x%08x\n", v); i = UPS_PORT_LINK_STATE_SET(XHCI_PS_PLS_GET(v)); switch (XHCI_PS_SPEED_GET(v)) { case 3: i |= UPS_HIGH_SPEED; break; case 2: i |= UPS_LOW_SPEED; break; case 1: /* FULL speed */ break; default: i |= UPS_OTHER_SPEED; break; } if (v & XHCI_PS_CCS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & XHCI_PS_PED) i |= UPS_PORT_ENABLED; if (v & XHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if (v & XHCI_PS_PR) i |= UPS_RESET; if (v & XHCI_PS_PP) { /* * The USB 3.0 RH is using the * USB 2.0's power bit */ i |= UPS_PORT_POWER; } USETW(sc->sc_hub_desc.ps.wPortStatus, i); i = 0; if (v & XHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & XHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & XHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (v & XHCI_PS_WRC) i |= UPS_C_BH_PORT_RESET; if (v & XHCI_PS_PRC) i |= UPS_C_PORT_RESET; if (v & XHCI_PS_PLC) i |= UPS_C_PORT_LINK_STATE; if (v & XHCI_PS_CEC) i |= UPS_C_PORT_CONFIG_ERROR; USETW(sc->sc_hub_desc.ps.wPortChange, i); len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): i = index >> 8; index &= 0x00FF; if ((index < 1) || (index > sc->sc_noport)) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTSC(index); v = XREAD4(sc, oper, port) & ~XHCI_PS_CLEAR; switch (value) { case UHF_PORT_U1_TIMEOUT: if (XHCI_PS_SPEED_GET(v) != 4) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTPMSC(index); v = XREAD4(sc, oper, port); v &= ~XHCI_PM3_U1TO_SET(0xFF); v |= XHCI_PM3_U1TO_SET(i); XWRITE4(sc, oper, port, v); break; case UHF_PORT_U2_TIMEOUT: if (XHCI_PS_SPEED_GET(v) != 4) { err = USB_ERR_IOERROR; goto done; } port = XHCI_PORTPMSC(index); v = XREAD4(sc, oper, port); v &= ~XHCI_PM3_U2TO_SET(0xFF); v |= XHCI_PM3_U2TO_SET(i); XWRITE4(sc, oper, port, v); break; case UHF_BH_PORT_RESET: XWRITE4(sc, oper, port, v | XHCI_PS_WPR); break; case UHF_PORT_LINK_STATE: XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(i) | XHCI_PS_LWS); /* 4ms settle time */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250); break; case UHF_PORT_ENABLE: DPRINTFN(3, "set port enable %d\n", index); break; case UHF_PORT_SUSPEND: DPRINTFN(6, "suspend port %u (LPM=%u)\n", index, i); j = XHCI_PS_SPEED_GET(v); if ((j < 1) || (j > 3)) { /* non-supported speed */ err = USB_ERR_IOERROR; goto done; } XWRITE4(sc, oper, port, v | XHCI_PS_PLS_SET(i ? 2 /* LPM */ : 3) | XHCI_PS_LWS); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); XWRITE4(sc, oper, port, v | XHCI_PS_PR); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); XWRITE4(sc, oper, port, v | XHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "set port test %d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "set port indicator %d\n", index); v &= ~XHCI_PS_PIC_SET(3); v |= XHCI_PS_PIC_SET(1); XWRITE4(sc, oper, port, v); break; default: err = USB_ERR_IOERROR; goto done; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: err = USB_ERR_IOERROR; goto done; } done: *plength = len; *pptr = ptr; return (err); } static void xhci_xfer_setup(struct usb_setup_params *parm) { struct usb_page_search page_info; struct usb_page_cache *pc; struct xhci_softc *sc; struct usb_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; sc = XHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * The proof for the "ntd" formula is illustrated like this: * * +------------------------------------+ * | | * | |remainder -> | * | +-----+---+ | * | | xxx | x | frm 0 | * | +-----+---++ | * | | xxx | xx | frm 1 | * | +-----+----+ | * | ... | * +------------------------------------+ * * "xxx" means a completely full USB transfer descriptor * * "x" and "xx" means a short USB packet * * For the remainder of an USB transfer modulo * "max_data_length" we need two USB transfer descriptors. * One to transfer the remaining data and one to finalise with * a zero length packet in case the "force_short_xfer" flag is * set. We only need two USB transfer descriptors in the case * where the transfer length of the first one is a factor of * "max_frame_size". The rest of the needed USB transfer * descriptors is given by the buffer size divided by the * maximum data payload. */ parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 16 * 3; parm->hc_max_frame_size = XHCI_TD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usbd_transfer_setup_sub(parm); if (xfer->flags_int.isochronous_xfr) { ntd = ((1 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } else if (xfer->flags_int.control_xfr) { ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_hc_frame_size)); } else { ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_hc_frame_size)); } alloc_dma_set: if (parm->err) return; /* * Allocate queue heads and transfer descriptors */ last_obj = NULL; if (usbd_transfer_setup_sub_malloc( parm, &pc, sizeof(struct xhci_td), XHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { struct xhci_td *td; usbd_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->td_self = page_info.physaddr; td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static usb_error_t xhci_configure_reset_endpoint(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); struct usb_page_search buf_inp; struct usb_device *udev; struct xhci_endpoint_ext *pepext; struct usb_endpoint_descriptor *edesc; struct usb_page_cache *pcinp; usb_error_t err; usb_stream_t stream_id; uint8_t index; uint8_t epno; pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); udev = xfer->xroot->udev; index = udev->controller_slot_id; pcinp = &sc->sc_hw.devs[index].input_pc; usbd_get_page(pcinp, 0, &buf_inp); edesc = xfer->endpoint->edesc; epno = edesc->bEndpointAddress; stream_id = xfer->stream_id; if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) epno |= UE_DIR_IN; epno = XHCI_EPNO2EPID(epno); if (epno == 0) return (USB_ERR_NO_PIPE); /* invalid */ XHCI_CMD_LOCK(sc); /* configure endpoint */ err = xhci_configure_endpoint_by_xfer(xfer); if (err != 0) { XHCI_CMD_UNLOCK(sc); return (err); } /* * Get the endpoint into the stopped state according to the * endpoint context state diagram in the XHCI specification: */ err = xhci_cmd_stop_ep(sc, 0, epno, index); if (err != 0) DPRINTF("Could not stop endpoint %u\n", epno); err = xhci_cmd_reset_ep(sc, 0, epno, index); if (err != 0) DPRINTF("Could not reset endpoint %u\n", epno); err = xhci_cmd_set_tr_dequeue_ptr(sc, (pepext->physaddr + (stream_id * sizeof(struct xhci_trb) * XHCI_MAX_TRANSFERS)) | XHCI_EPCTX_2_DCS_SET(1), stream_id, epno, index); if (err != 0) DPRINTF("Could not set dequeue ptr for endpoint %u\n", epno); /* * Get the endpoint into the running state according to the * endpoint context state diagram in the XHCI specification: */ xhci_configure_mask(udev, (1U << epno) | 1U, 0); err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index); if (err != 0) DPRINTF("Could not configure endpoint %u\n", epno); err = xhci_cmd_configure_ep(sc, buf_inp.physaddr, 0, index); if (err != 0) DPRINTF("Could not configure endpoint %u\n", epno); XHCI_CMD_UNLOCK(sc); return (0); } static void xhci_xfer_unsetup(struct usb_xfer *xfer) { return; } static void xhci_start_dma_delay(struct usb_xfer *xfer) { struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); /* put transfer on interrupt queue (again) */ usbd_transfer_enqueue(&sc->sc_bus.intr_q, xfer); (void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus), &sc->sc_config_msg[0], &sc->sc_config_msg[1]); } static void xhci_configure_msg(struct usb_proc_msg *pm) { struct xhci_softc *sc; struct xhci_endpoint_ext *pepext; struct usb_xfer *xfer; sc = XHCI_BUS2SC(((struct usb_bus_msg *)pm)->bus); restart: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { pepext = xhci_get_endpoint_ext(xfer->xroot->udev, xfer->endpoint->edesc); if ((pepext->trb_halted != 0) || (pepext->trb_running == 0)) { uint16_t i; /* clear halted and running */ pepext->trb_halted = 0; pepext->trb_running = 0; /* nuke remaining buffered transfers */ for (i = 0; i != (XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS); i++) { /* * NOTE: We need to use the timeout * error code here else existing * isochronous clients can get * confused: */ if (pepext->xfer[i] != NULL) { xhci_device_done(pepext->xfer[i], USB_ERR_TIMEOUT); } } /* * NOTE: The USB transfer cannot vanish in * this state! */ USB_BUS_UNLOCK(&sc->sc_bus); xhci_configure_reset_endpoint(xfer); USB_BUS_LOCK(&sc->sc_bus); /* check if halted is still cleared */ if (pepext->trb_halted == 0) { pepext->trb_running = 1; memset(pepext->trb_index, 0, sizeof(pepext->trb_index)); } goto restart; } if (xfer->flags_int.did_dma_delay) { /* remove transfer from interrupt queue (again) */ usbd_transfer_dequeue(xfer); /* we are finally done */ usb_dma_delay_done_cb(xfer); /* queue changed - restart */ goto restart; } } TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* try to insert xfer on HW queue */ xhci_transfer_insert(xfer); /* try to multi buffer */ xhci_device_generic_multi_enter(xfer->endpoint, xfer->stream_id, NULL); } } static void xhci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { struct xhci_endpoint_ext *pepext; DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d\n", ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode); if (udev->parent_hub == NULL) { /* root HUB has special endpoint handling */ return; } ep->methods = &xhci_device_generic_methods; pepext = xhci_get_endpoint_ext(udev, edesc); USB_BUS_LOCK(udev->bus); pepext->trb_halted = 1; pepext->trb_running = 0; USB_BUS_UNLOCK(udev->bus); } static void xhci_ep_uninit(struct usb_device *udev, struct usb_endpoint *ep) { } static void xhci_ep_clear_stall(struct usb_device *udev, struct usb_endpoint *ep) { struct xhci_endpoint_ext *pepext; DPRINTF("\n"); if (udev->flags.usb_mode != USB_MODE_HOST) { /* not supported */ return; } if (udev->parent_hub == NULL) { /* root HUB has special endpoint handling */ return; } pepext = xhci_get_endpoint_ext(udev, ep->edesc); USB_BUS_LOCK(udev->bus); pepext->trb_halted = 1; pepext->trb_running = 0; USB_BUS_UNLOCK(udev->bus); } static usb_error_t xhci_device_init(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); usb_error_t err; uint8_t temp; /* no init for root HUB */ if (udev->parent_hub == NULL) return (0); XHCI_CMD_LOCK(sc); /* set invalid default */ udev->controller_slot_id = sc->sc_noslot + 1; /* try to get a new slot ID from the XHCI */ err = xhci_cmd_enable_slot(sc, &temp); if (err) { XHCI_CMD_UNLOCK(sc); return (err); } if (temp > sc->sc_noslot) { XHCI_CMD_UNLOCK(sc); return (USB_ERR_BAD_ADDRESS); } if (sc->sc_hw.devs[temp].state != XHCI_ST_DISABLED) { DPRINTF("slot %u already allocated.\n", temp); XHCI_CMD_UNLOCK(sc); return (USB_ERR_BAD_ADDRESS); } /* store slot ID for later reference */ udev->controller_slot_id = temp; /* reset data structure */ memset(&sc->sc_hw.devs[temp], 0, sizeof(sc->sc_hw.devs[0])); /* set mark slot allocated */ sc->sc_hw.devs[temp].state = XHCI_ST_ENABLED; err = xhci_alloc_device_ext(udev); XHCI_CMD_UNLOCK(sc); /* get device into default state */ if (err == 0) err = xhci_set_address(udev, NULL, 0); return (err); } static void xhci_device_uninit(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; /* no init for root HUB */ if (udev->parent_hub == NULL) return; XHCI_CMD_LOCK(sc); index = udev->controller_slot_id; if (index <= sc->sc_noslot) { xhci_cmd_disable_slot(sc, index); sc->sc_hw.devs[index].state = XHCI_ST_DISABLED; /* free device extension */ xhci_free_device_ext(udev); } XHCI_CMD_UNLOCK(sc); } static void xhci_get_dma_delay(struct usb_device *udev, uint32_t *pus) { /* * Wait until the hardware has finished any possible use of * the transfer descriptor(s) */ *pus = 2048; /* microseconds */ } static void xhci_device_resume(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t n; uint8_t p; DPRINTF("\n"); /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; XHCI_CMD_LOCK(sc); /* blindly resume all endpoints */ USB_BUS_LOCK(udev->bus); for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) { for (p = 0; p != XHCI_MAX_STREAMS; p++) { XWRITE4(sc, door, XHCI_DOORBELL(index), n | XHCI_DB_SID_SET(p)); } } USB_BUS_UNLOCK(udev->bus); XHCI_CMD_UNLOCK(sc); } static void xhci_device_suspend(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); uint8_t index; uint8_t n; usb_error_t err; DPRINTF("\n"); /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; XHCI_CMD_LOCK(sc); /* blindly suspend all endpoints */ for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) { err = xhci_cmd_stop_ep(sc, 1, n, index); if (err != 0) { DPRINTF("Failed to suspend endpoint " "%u on slot %u (ignored).\n", n, index); } } XHCI_CMD_UNLOCK(sc); } static void xhci_set_hw_power(struct usb_bus *bus) { DPRINTF("\n"); } static void xhci_device_state_change(struct usb_device *udev) { struct xhci_softc *sc = XHCI_BUS2SC(udev->bus); struct usb_page_search buf_inp; usb_error_t err; uint8_t index; /* check for root HUB */ if (udev->parent_hub == NULL) return; index = udev->controller_slot_id; DPRINTF("\n"); if (usb_get_device_state(udev) == USB_STATE_CONFIGURED) { err = uhub_query_info(udev, &sc->sc_hw.devs[index].nports, &sc->sc_hw.devs[index].tt); if (err != 0) sc->sc_hw.devs[index].nports = 0; } XHCI_CMD_LOCK(sc); switch (usb_get_device_state(udev)) { case USB_STATE_POWERED: if (sc->sc_hw.devs[index].state == XHCI_ST_DEFAULT) break; /* set default state */ sc->sc_hw.devs[index].state = XHCI_ST_DEFAULT; /* reset number of contexts */ sc->sc_hw.devs[index].context_num = 0; err = xhci_cmd_reset_dev(sc, index); if (err != 0) { DPRINTF("Device reset failed " "for slot %u.\n", index); } break; case USB_STATE_ADDRESSED: if (sc->sc_hw.devs[index].state == XHCI_ST_ADDRESSED) break; sc->sc_hw.devs[index].state = XHCI_ST_ADDRESSED; err = xhci_cmd_configure_ep(sc, 0, 1, index); if (err) { DPRINTF("Failed to deconfigure " "slot %u.\n", index); } break; case USB_STATE_CONFIGURED: if (sc->sc_hw.devs[index].state == XHCI_ST_CONFIGURED) break; /* set configured state */ sc->sc_hw.devs[index].state = XHCI_ST_CONFIGURED; /* reset number of contexts */ sc->sc_hw.devs[index].context_num = 0; usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp); xhci_configure_mask(udev, 3, 0); err = xhci_configure_device(udev); if (err != 0) { DPRINTF("Could not configure device " "at slot %u.\n", index); } err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index); if (err != 0) { DPRINTF("Could not evaluate device " "context at slot %u.\n", index); } break; default: break; } XHCI_CMD_UNLOCK(sc); } static usb_error_t xhci_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { switch (ep_mode) { case USB_EP_MODE_DEFAULT: return (0); case USB_EP_MODE_STREAMS: if (xhcistreams == 0 || (ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK || udev->speed != USB_SPEED_SUPER) return (USB_ERR_INVAL); return (0); default: return (USB_ERR_INVAL); } } static const struct usb_bus_methods xhci_bus_methods = { .endpoint_init = xhci_ep_init, .endpoint_uninit = xhci_ep_uninit, .xfer_setup = xhci_xfer_setup, .xfer_unsetup = xhci_xfer_unsetup, .get_dma_delay = xhci_get_dma_delay, .device_init = xhci_device_init, .device_uninit = xhci_device_uninit, .device_resume = xhci_device_resume, .device_suspend = xhci_device_suspend, .set_hw_power = xhci_set_hw_power, .roothub_exec = xhci_roothub_exec, .xfer_poll = xhci_do_poll, .start_dma_delay = xhci_start_dma_delay, .set_address = xhci_set_address, .clear_stall = xhci_ep_clear_stall, .device_state_change = xhci_device_state_change, .set_hw_power_sleep = xhci_set_hw_power_sleep, .set_endpoint_mode = xhci_set_endpoint_mode, }; Index: projects/building-blocks/sys/dev/usb/usb_core.h =================================================================== --- projects/building-blocks/sys/dev/usb/usb_core.h (revision 278195) +++ projects/building-blocks/sys/dev/usb/usb_core.h (revision 278196) @@ -1,191 +1,192 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. */ /* * Including this file is mandatory for all USB related c-files in the kernel. */ #ifndef _USB_CORE_H_ #define _USB_CORE_H_ /* * The following macro will tell if an USB transfer is currently * receiving or transferring data. */ #define USB_GET_DATA_ISREAD(xfer) ((xfer)->flags_int.usb_mode == \ USB_MODE_DEVICE ? (((xfer)->endpointno & UE_DIR_IN) ? 0 : 1) : \ (((xfer)->endpointno & UE_DIR_IN) ? 1 : 0)) /* macros */ #define USB_BUS_LOCK(_b) mtx_lock(&(_b)->bus_mtx) #define USB_BUS_UNLOCK(_b) mtx_unlock(&(_b)->bus_mtx) #define USB_BUS_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_mtx, _t) #define USB_BUS_SPIN_LOCK(_b) mtx_lock_spin(&(_b)->bus_spin_lock) #define USB_BUS_SPIN_UNLOCK(_b) mtx_unlock_spin(&(_b)->bus_spin_lock) #define USB_BUS_SPIN_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_spin_lock, _t) #define USB_XFER_LOCK(_x) mtx_lock((_x)->xroot->xfer_mtx) #define USB_XFER_UNLOCK(_x) mtx_unlock((_x)->xroot->xfer_mtx) #define USB_XFER_LOCK_ASSERT(_x, _t) mtx_assert((_x)->xroot->xfer_mtx, _t) /* helper for converting pointers to integers */ #define USB_P2U(ptr) \ (((const uint8_t *)(ptr)) - ((const uint8_t *)0)) /* helper for computing offsets */ #define USB_ADD_BYTES(ptr,size) \ ((void *)(USB_P2U(ptr) + (size))) /* debug macro */ #define USB_ASSERT KASSERT /* structure prototypes */ struct file; struct usb_bus; struct usb_device; struct usb_device_request; struct usb_page; struct usb_page_cache; struct usb_xfer; struct usb_xfer_root; struct usb_string_lang; /* typedefs */ /* structures */ /* * The following structure defines a set of internal USB transfer * flags. */ struct usb_xfer_flags_int { enum usb_hc_mode usb_mode; /* shadow copy of "udev->usb_mode" */ uint16_t control_rem; /* remainder in bytes */ uint8_t open:1; /* set if USB pipe has been opened */ uint8_t transferring:1; /* set if an USB transfer is in * progress */ uint8_t did_dma_delay:1; /* set if we waited for HW DMA */ uint8_t did_close:1; /* set if we closed the USB transfer */ uint8_t draining:1; /* set if we are draining an USB * transfer */ uint8_t started:1; /* keeps track of started or stopped */ uint8_t bandwidth_reclaimed:1; uint8_t control_xfr:1; /* set if control transfer */ uint8_t control_hdr:1; /* set if control header should be * sent */ uint8_t control_act:1; /* set if control transfer is active */ uint8_t control_stall:1; /* set if control transfer should be stalled */ + uint8_t control_did_data:1; /* set if control DATA has been transferred */ uint8_t short_frames_ok:1; /* filtered version */ uint8_t short_xfer_ok:1; /* filtered version */ #if USB_HAVE_BUSDMA uint8_t bdma_enable:1; /* filtered version (only set if * hardware supports DMA) */ uint8_t bdma_no_post_sync:1; /* set if the USB callback wrapper * should not do the BUS-DMA post sync * operation */ uint8_t bdma_setup:1; /* set if BUS-DMA has been setup */ #endif uint8_t isochronous_xfr:1; /* set if isochronous transfer */ uint8_t curr_dma_set:1; /* used by USB HC/DC driver */ uint8_t can_cancel_immed:1; /* set if USB transfer can be * cancelled immediately */ uint8_t doing_callback:1; /* set if executing the callback */ uint8_t maxp_was_clamped:1; /* set if the max packet size * was outside its allowed range */ }; /* * The following structure defines an USB transfer. */ struct usb_xfer { struct usb_callout timeout_handle; TAILQ_ENTRY(usb_xfer) wait_entry; /* used at various places */ struct usb_page_cache *buf_fixup; /* fixup buffer(s) */ struct usb_xfer_queue *wait_queue; /* pointer to queue that we * are waiting on */ struct usb_page *dma_page_ptr; struct usb_endpoint *endpoint; /* our USB endpoint */ struct usb_xfer_root *xroot; /* used by HC driver */ void *qh_start[2]; /* used by HC driver */ void *td_start[2]; /* used by HC driver */ void *td_transfer_first; /* used by HC driver */ void *td_transfer_last; /* used by HC driver */ void *td_transfer_cache; /* used by HC driver */ void *priv_sc; /* device driver data pointer 1 */ void *priv_fifo; /* device driver data pointer 2 */ void *local_buffer; usb_frlength_t *frlengths; struct usb_page_cache *frbuffers; usb_callback_t *callback; usb_frlength_t max_hc_frame_size; usb_frlength_t max_data_length; usb_frlength_t sumlen; /* sum of all lengths in bytes */ usb_frlength_t actlen; /* actual length in bytes */ usb_timeout_t timeout; /* milliseconds */ usb_frcount_t max_frame_count; /* initial value of "nframes" after * setup */ usb_frcount_t nframes; /* number of USB frames to transfer */ usb_frcount_t aframes; /* actual number of USB frames * transferred */ usb_stream_t stream_id; /* USB3.0 specific field */ uint16_t max_packet_size; uint16_t max_frame_size; uint16_t qh_pos; uint16_t isoc_time_complete; /* in ms */ usb_timeout_t interval; /* milliseconds */ uint8_t address; /* physical USB address */ uint8_t endpointno; /* physical USB endpoint */ uint8_t max_packet_count; uint8_t usb_state; uint8_t fps_shift; /* down shift of FPS, 0..3 */ usb_error_t error; struct usb_xfer_flags flags; struct usb_xfer_flags_int flags_int; }; /* external variables */ extern struct mtx usb_ref_lock; extern const struct usb_string_lang usb_string_lang_en; /* typedefs */ typedef struct malloc_type *usb_malloc_type; /* prototypes */ #endif /* _USB_CORE_H_ */ Index: projects/building-blocks/sys/dev/usb/usb_msctest.c =================================================================== --- projects/building-blocks/sys/dev/usb/usb_msctest.c (revision 278195) +++ projects/building-blocks/sys/dev/usb/usb_msctest.c (revision 278196) @@ -1,973 +1,988 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008,2011 Hans Petter Selasky. 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 following file contains code that will detect USB autoinstall * disks. * * TODO: Potentially we could add code to automatically detect USB * mass storage quirks for not supported SCSI commands! */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ enum { ST_COMMAND, ST_DATA_RD, ST_DATA_RD_CS, ST_DATA_WR, ST_DATA_WR_CS, ST_STATUS, ST_MAX, }; enum { DIR_IN, DIR_OUT, DIR_NONE, }; #define SCSI_MAX_LEN MAX(SCSI_FIXED_BLOCK_SIZE, USB_MSCTEST_BULK_SIZE) #define SCSI_INQ_LEN 0x24 #define SCSI_SENSE_LEN 0xFF #define SCSI_FIXED_BLOCK_SIZE 512 /* bytes */ static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 }; static uint8_t scsi_rezero_init[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 }; static uint8_t scsi_ztestor_eject[] = { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00 }; static uint8_t scsi_cmotech_eject[] = { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43, 0x48, 0x47 }; static uint8_t scsi_huawei_eject[] = { 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_huawei_eject2[] = { 0x11, 0x06, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_tct_eject[] = { 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 }; static uint8_t scsi_sync_cache[] = { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_request_sense[] = { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_read_capacity[] = { 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static uint8_t scsi_prevent_removal[] = { 0x1e, 0, 0, 0, 1, 0 }; +static uint8_t scsi_allow_removal[] = { 0x1e, 0, 0, 0, 0, 0 }; #ifndef USB_MSCTEST_BULK_SIZE #define USB_MSCTEST_BULK_SIZE 64 /* dummy */ #endif #define ERR_CSW_FAILED -1 /* Command Block Wrapper */ struct bbb_cbw { uDWord dCBWSignature; #define CBWSIGNATURE 0x43425355 uDWord dCBWTag; uDWord dCBWDataTransferLength; uByte bCBWFlags; #define CBWFLAGS_OUT 0x00 #define CBWFLAGS_IN 0x80 uByte bCBWLUN; uByte bCDBLength; #define CBWCDBLENGTH 16 uByte CBWCDB[CBWCDBLENGTH]; } __packed; /* Command Status Wrapper */ struct bbb_csw { uDWord dCSWSignature; #define CSWSIGNATURE 0x53425355 uDWord dCSWTag; uDWord dCSWDataResidue; uByte bCSWStatus; #define CSWSTATUS_GOOD 0x0 #define CSWSTATUS_FAILED 0x1 #define CSWSTATUS_PHASE 0x2 } __packed; struct bbb_transfer { struct mtx mtx; struct cv cv; struct bbb_cbw *cbw; struct bbb_csw *csw; struct usb_xfer *xfer[ST_MAX]; uint8_t *data_ptr; usb_size_t data_len; /* bytes */ usb_size_t data_rem; /* bytes */ usb_timeout_t data_timeout; /* ms */ usb_frlength_t actlen; /* bytes */ usb_frlength_t buffer_size; /* bytes */ uint8_t cmd_len; /* bytes */ uint8_t dir; uint8_t lun; uint8_t state; uint8_t status_try; int error; uint8_t *buffer; }; static usb_callback_t bbb_command_callback; static usb_callback_t bbb_data_read_callback; static usb_callback_t bbb_data_rd_cs_callback; static usb_callback_t bbb_data_write_callback; static usb_callback_t bbb_data_wr_cs_callback; static usb_callback_t bbb_status_callback; static void bbb_done(struct bbb_transfer *, int); static void bbb_transfer_start(struct bbb_transfer *, uint8_t); static void bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t, uint8_t); static int bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t, void *, size_t, void *, size_t, usb_timeout_t); static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t); static void bbb_detach(struct bbb_transfer *); static const struct usb_config bbb_config[ST_MAX] = { [ST_COMMAND] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = sizeof(struct bbb_cbw), .callback = &bbb_command_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = SCSI_MAX_LEN, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, .callback = &bbb_data_read_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &bbb_data_rd_cs_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, [ST_DATA_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = SCSI_MAX_LEN, .flags = {.ext_buffer = 1,.proxy_buffer = 1,}, .callback = &bbb_data_write_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &bbb_data_wr_cs_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, [ST_STATUS] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(struct bbb_csw), .flags = {.short_xfer_ok = 1,}, .callback = &bbb_status_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, }; static void bbb_done(struct bbb_transfer *sc, int error) { sc->error = error; sc->state = ST_COMMAND; sc->status_try = 1; cv_signal(&sc->cv); } static void bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) { sc->state = xfer_index; usbd_transfer_start(sc->xfer[xfer_index]); } static void bbb_data_clear_stall_callback(struct usb_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: bbb_transfer_start(sc, next_xfer); break; default: bbb_done(sc, USB_ERR_STALLED); break; } } } static void bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); uint32_t tag; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: bbb_transfer_start (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : (sc->dir == DIR_OUT) ? ST_DATA_WR : ST_STATUS)); break; case USB_ST_SETUP: sc->status_try = 0; tag = UGETDW(sc->cbw->dCBWTag) + 1; USETDW(sc->cbw->dCBWSignature, CBWSIGNATURE); USETDW(sc->cbw->dCBWTag, tag); USETDW(sc->cbw->dCBWDataTransferLength, (uint32_t)sc->data_len); sc->cbw->bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); sc->cbw->bCBWLUN = sc->lun; sc->cbw->bCDBLength = sc->cmd_len; if (sc->cbw->bCDBLength > sizeof(sc->cbw->CBWCDB)) { sc->cbw->bCDBLength = sizeof(sc->cbw->CBWCDB); DPRINTFN(0, "Truncating long command\n"); } usbd_xfer_set_frame_len(xfer, 0, sizeof(struct bbb_cbw)); usbd_transfer_submit(xfer); break; default: /* Error */ bbb_done(sc, error); break; } } static void bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->data_rem -= actlen; sc->data_ptr += actlen; sc->actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->data_rem = 0; } case USB_ST_SETUP: DPRINTF("max_bulk=%d, data_rem=%d\n", max_bulk, sc->data_rem); if (sc->data_rem == 0) { bbb_transfer_start(sc, ST_STATUS); break; } if (max_bulk > sc->data_rem) { max_bulk = sc->data_rem; } usbd_xfer_set_timeout(xfer, sc->data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); usbd_transfer_submit(xfer); break; default: /* Error */ if (error == USB_ERR_CANCELLED) { bbb_done(sc, error); } else { bbb_transfer_start(sc, ST_DATA_RD_CS); } break; } } static void bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error) { bbb_data_clear_stall_callback(xfer, ST_STATUS, ST_DATA_RD); } static void bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->data_rem -= actlen; sc->data_ptr += actlen; sc->actlen += actlen; if (actlen < sumlen) { /* short transfer */ sc->data_rem = 0; } case USB_ST_SETUP: DPRINTF("max_bulk=%d, data_rem=%d\n", max_bulk, sc->data_rem); if (sc->data_rem == 0) { bbb_transfer_start(sc, ST_STATUS); break; } if (max_bulk > sc->data_rem) { max_bulk = sc->data_rem; } usbd_xfer_set_timeout(xfer, sc->data_timeout); usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk); usbd_transfer_submit(xfer); break; default: /* Error */ if (error == USB_ERR_CANCELLED) { bbb_done(sc, error); } else { bbb_transfer_start(sc, ST_DATA_WR_CS); } break; } } static void bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error) { bbb_data_clear_stall_callback(xfer, ST_STATUS, ST_DATA_WR); } static void bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); int actlen; int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* very simple status check */ if (actlen < (int)sizeof(struct bbb_csw)) { bbb_done(sc, USB_ERR_SHORT_XFER); } else if (sc->csw->bCSWStatus == CSWSTATUS_GOOD) { bbb_done(sc, 0); /* success */ } else { bbb_done(sc, ERR_CSW_FAILED); /* error */ } break; case USB_ST_SETUP: usbd_xfer_set_frame_len(xfer, 0, sizeof(struct bbb_csw)); usbd_transfer_submit(xfer); break; default: DPRINTF("Failed to read CSW: %s, try %d\n", usbd_errstr(error), sc->status_try); if (error == USB_ERR_CANCELLED || sc->status_try) { bbb_done(sc, error); } else { sc->status_try = 1; bbb_transfer_start(sc, ST_DATA_RD_CS); } break; } } /*------------------------------------------------------------------------* * bbb_command_start - execute a SCSI command synchronously * * Return values * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len, usb_timeout_t data_timeout) { sc->lun = lun; sc->dir = data_len ? dir : DIR_NONE; sc->data_ptr = data_ptr; sc->data_len = data_len; sc->data_rem = data_len; sc->data_timeout = (data_timeout + USB_MS_HZ); sc->actlen = 0; sc->error = 0; sc->cmd_len = cmd_len; memset(&sc->cbw->CBWCDB, 0, sizeof(sc->cbw->CBWCDB)); memcpy(&sc->cbw->CBWCDB, cmd_ptr, cmd_len); DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw->CBWCDB, ":"); mtx_lock(&sc->mtx); usbd_transfer_start(sc->xfer[sc->state]); while (usbd_transfer_pending(sc->xfer[sc->state])) { cv_wait(&sc->cv, &sc->mtx); } mtx_unlock(&sc->mtx); return (sc->error); } static struct bbb_transfer * bbb_attach(struct usb_device *udev, uint8_t iface_index) { struct usb_interface *iface; struct usb_interface_descriptor *id; struct bbb_transfer *sc; usb_error_t err; #if USB_HAVE_MSCTEST_DETACH uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); /* * Make sure any driver which is hooked up to this interface, * like umass is gone: */ usb_detach_device(udev, iface_index, 0); if (do_unlock) usbd_enum_unlock(udev); #endif iface = usbd_get_iface(udev, iface_index); if (iface == NULL) return (NULL); id = iface->idesc; if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return (NULL); switch (id->bInterfaceSubClass) { case UISUBCLASS_SCSI: case UISUBCLASS_UFI: case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: break; default: return (NULL); } switch (id->bInterfaceProtocol) { case UIPROTO_MASS_BBB_OLD: case UIPROTO_MASS_BBB: break; default: return (NULL); } sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); cv_init(&sc->cv, "WBBB"); err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config, ST_MAX, sc, &sc->mtx); if (err) { bbb_detach(sc); return (NULL); } /* store pointer to DMA buffers */ sc->buffer = usbd_xfer_get_frame_buffer( sc->xfer[ST_DATA_RD], 0); sc->buffer_size = usbd_xfer_max_len(sc->xfer[ST_DATA_RD]); sc->cbw = usbd_xfer_get_frame_buffer( sc->xfer[ST_COMMAND], 0); sc->csw = usbd_xfer_get_frame_buffer( sc->xfer[ST_STATUS], 0); return (sc); } static void bbb_detach(struct bbb_transfer *sc) { usbd_transfer_unsetup(sc->xfer, ST_MAX); mtx_destroy(&sc->mtx); cv_destroy(&sc->cv); free(sc, M_USB); } /*------------------------------------------------------------------------* * usb_iface_is_cdrom * * Return values: * 1: This interface is an auto install disk (CD-ROM) * 0: Not an auto install disk. *------------------------------------------------------------------------*/ int usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index) { struct bbb_transfer *sc; uint8_t timeout; uint8_t is_cdrom; uint8_t sid_type; int err; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (0); is_cdrom = 0; timeout = 4; /* tries */ while (--timeout) { err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), USB_MS_HZ); if (err == 0 && sc->actlen > 0) { sid_type = sc->buffer[0] & 0x1F; if (sid_type == 0x05) is_cdrom = 1; break; } else if (err != ERR_CSW_FAILED) break; /* non retryable error */ usb_pause_mtx(NULL, hz); } bbb_detach(sc); return (is_cdrom); } static uint8_t usb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index) { struct usb_device_request req; usb_error_t err; uint8_t buf = 0; /* The Get Max Lun command is a class-specific request. */ req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = 0xFE; /* GET_MAX_LUN */ USETW(req.wValue, 0); req.wIndex[0] = iface_index; req.wIndex[1] = 0; USETW(req.wLength, 1); err = usbd_do_request(udev, NULL, &req, &buf); if (err) buf = 0; return (buf); } usb_error_t usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index) { struct bbb_transfer *sc; uint8_t timeout; uint8_t is_no_direct; uint8_t sid_type; int err; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (0); /* * Some devices need a delay after that the configuration * value is set to function properly: */ usb_pause_mtx(NULL, hz); if (usb_msc_get_max_lun(udev, iface_index) == 0) { DPRINTF("Device has only got one LUN.\n"); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN); } is_no_direct = 1; for (timeout = 4; timeout != 0; timeout--) { err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), USB_MS_HZ); if (err == 0 && sc->actlen > 0) { sid_type = sc->buffer[0] & 0x1F; if (sid_type == 0x00) is_no_direct = 0; break; } else if (err != ERR_CSW_FAILED) { DPRINTF("Device is not responding " "properly to SCSI INQUIRY command.\n"); goto error; /* non retryable error */ } usb_pause_mtx(NULL, hz); } if (is_no_direct) { DPRINTF("Device is not direct access.\n"); goto done; } err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), USB_MS_HZ); if (err != 0) { + if (err != ERR_CSW_FAILED) + goto error; + DPRINTF("Test unit ready failed\n"); + } + err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0, + &scsi_prevent_removal, sizeof(scsi_prevent_removal), + USB_MS_HZ); + + if (err == 0) { + err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0, + &scsi_allow_removal, sizeof(scsi_allow_removal), + USB_MS_HZ); + } + + if (err != 0) { if (err != ERR_CSW_FAILED) goto error; + DPRINTF("Device doesn't handle prevent and allow removal\n"); + usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW); } + timeout = 1; retry_sync_cache: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_sync_cache, sizeof(scsi_sync_cache), USB_MS_HZ); if (err != 0) { if (err != ERR_CSW_FAILED) goto error; - DPRINTF("Device doesn't handle synchronize cache " - "and prevent allow medium removal\n"); + DPRINTF("Device doesn't handle synchronize cache\n"); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); - usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW); } else { /* * Certain Kingston memory sticks fail the first * read capacity after a synchronize cache command * has been issued. Disable the synchronize cache * command for such devices. */ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8, &scsi_read_capacity, sizeof(scsi_read_capacity), USB_MS_HZ); if (err != 0) { if (err != ERR_CSW_FAILED) goto error; err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8, &scsi_read_capacity, sizeof(scsi_read_capacity), USB_MS_HZ); if (err == 0) { if (timeout--) goto retry_sync_cache; DPRINTF("Device most likely doesn't " - "handle synchronize cache nor" - "prevent allow medium removal\n"); + "handle synchronize cache\n"); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); - usbd_add_dynamic_quirk(udev, - UQ_MSC_NO_PREVENT_ALLOW); } else { if (err != ERR_CSW_FAILED) goto error; } } } /* clear sense status of any failed commands on the device */ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), USB_MS_HZ); DPRINTF("Inquiry = %d\n", err); if (err != 0) { if (err != ERR_CSW_FAILED) goto error; } err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, SCSI_SENSE_LEN, &scsi_request_sense, sizeof(scsi_request_sense), USB_MS_HZ); DPRINTF("Request sense = %d\n", err); if (err != 0) { if (err != ERR_CSW_FAILED) goto error; } done: bbb_detach(sc); return (0); error: bbb_detach(sc); DPRINTF("Device did not respond, enabling all quirks\n"); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW); usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY); /* Need to re-enumerate the device */ usbd_req_re_enumerate(udev, NULL); return (USB_ERR_STALLED); } usb_error_t usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method) { struct bbb_transfer *sc; usb_error_t err; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (USB_ERR_INVAL); switch (method) { case MSC_EJECT_STOPUNIT: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), USB_MS_HZ); DPRINTF("Test unit ready status: %s\n", usbd_errstr(err)); err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_start_stop_unit, sizeof(scsi_start_stop_unit), USB_MS_HZ); break; case MSC_EJECT_REZERO: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_rezero_init, sizeof(scsi_rezero_init), USB_MS_HZ); break; case MSC_EJECT_ZTESTOR: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_ztestor_eject, sizeof(scsi_ztestor_eject), USB_MS_HZ); break; case MSC_EJECT_CMOTECH: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_cmotech_eject, sizeof(scsi_cmotech_eject), USB_MS_HZ); break; case MSC_EJECT_HUAWEI: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_huawei_eject, sizeof(scsi_huawei_eject), USB_MS_HZ); break; case MSC_EJECT_HUAWEI2: err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, &scsi_huawei_eject2, sizeof(scsi_huawei_eject2), USB_MS_HZ); break; case MSC_EJECT_TCT: /* * TCTMobile needs DIR_IN flag. To get it, we * supply a dummy data with the command. */ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, sc->buffer_size, &scsi_tct_eject, sizeof(scsi_tct_eject), USB_MS_HZ); break; default: DPRINTF("Unknown eject method (%d)\n", method); bbb_detach(sc); return (USB_ERR_INVAL); } DPRINTF("Eject CD command status: %s\n", usbd_errstr(err)); bbb_detach(sc); return (0); } usb_error_t usb_msc_read_10(struct usb_device *udev, uint8_t iface_index, uint32_t lba, uint32_t blocks, void *buffer) { struct bbb_transfer *sc; uint8_t cmd[10]; usb_error_t err; cmd[0] = 0x28; /* READ_10 */ cmd[1] = 0; cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba >> 0; cmd[6] = 0; cmd[7] = blocks >> 8; cmd[8] = blocks; cmd[9] = 0; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (USB_ERR_INVAL); err = bbb_command_start(sc, DIR_IN, 0, buffer, blocks * SCSI_FIXED_BLOCK_SIZE, cmd, 10, USB_MS_HZ); bbb_detach(sc); return (err); } usb_error_t usb_msc_write_10(struct usb_device *udev, uint8_t iface_index, uint32_t lba, uint32_t blocks, void *buffer) { struct bbb_transfer *sc; uint8_t cmd[10]; usb_error_t err; cmd[0] = 0x2a; /* WRITE_10 */ cmd[1] = 0; cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba >> 0; cmd[6] = 0; cmd[7] = blocks >> 8; cmd[8] = blocks; cmd[9] = 0; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (USB_ERR_INVAL); err = bbb_command_start(sc, DIR_OUT, 0, buffer, blocks * SCSI_FIXED_BLOCK_SIZE, cmd, 10, USB_MS_HZ); bbb_detach(sc); return (err); } usb_error_t usb_msc_read_capacity(struct usb_device *udev, uint8_t iface_index, uint32_t *lba_last, uint32_t *block_size) { struct bbb_transfer *sc; usb_error_t err; sc = bbb_attach(udev, iface_index); if (sc == NULL) return (USB_ERR_INVAL); err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 8, &scsi_read_capacity, sizeof(scsi_read_capacity), USB_MS_HZ); *lba_last = (sc->buffer[0] << 24) | (sc->buffer[1] << 16) | (sc->buffer[2] << 8) | (sc->buffer[3]); *block_size = (sc->buffer[4] << 24) | (sc->buffer[5] << 16) | (sc->buffer[6] << 8) | (sc->buffer[7]); /* we currently only support one block size */ if (*block_size != SCSI_FIXED_BLOCK_SIZE) err = USB_ERR_INVAL; bbb_detach(sc); return (err); } Index: projects/building-blocks/sys/dev/usb/usb_transfer.c =================================================================== --- projects/building-blocks/sys/dev/usb/usb_transfer.c (revision 278195) +++ projects/building-blocks/sys/dev/usb/usb_transfer.c (revision 278196) @@ -1,3458 +1,3509 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ +#ifdef USB_DMA_SINGLE_ALLOC + n_obj = 1; +#else n_obj = (USB_PAGE_SIZE / size); +#endif /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = ((count + n_obj - 1) / n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } - if (ppc) { - *ppc = parm->xfer_page_cache_ptr; + if (ppc != NULL) { + if (n_obj != 1) + *ppc = parm->xfer_page_cache_ptr; + else + *ppc = parm->dma_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; - for (x = 0; x != n_dma_pc; x++) { + if (n_obj == 1) { + /* + * Avoid mapping memory twice if only a single object + * should be allocated per page cache: + */ + for (x = 0; x != n_dma_pc; x++) { + if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, + pg, z, align)) { + return (1); /* failure */ + } + /* Make room for one DMA page cache and "n_dma_pg" pages */ + parm->dma_page_cache_ptr++; + pg += n_dma_pg; + } + } else { + for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; - /* Make room for one DMA page cache and one page */ + /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } + } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to alot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == 0) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ do_unlock = usbd_enum_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, sizeof(*parm)); parm->udev = udev; parm->speed = usbd_get_speed(udev); parm->hc_max_packet_count = 1; if (parm->speed >= USB_SPEED_MAX) { parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) usbd_enum_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* + * usbd_control_transfer_did_data + * + * This function returns non-zero if a control endpoint has + * transferred the first DATA packet after the SETUP packet. + * Else it returns zero. + *------------------------------------------------------------------------*/ +static uint8_t +usbd_control_transfer_did_data(struct usb_xfer *xfer) +{ + struct usb_device_request req; + + /* SETUP packet is not yet sent */ + if (xfer->flags_int.control_hdr != 0) + return (0); + + /* copy out the USB request header */ + usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); + + /* compare remainder to the initial value */ + return (xfer->flags_int.control_rem != UGETW(req.wLength)); +} + +/*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } + + /* update did data flag */ + + xfer->flags_int.control_did_data = + usbd_control_transfer_did_data(xfer); /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if (!mtx_owned(info->xfer_mtx) && !SCHEDULER_STOPPED()) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. */ DPRINTFN(3, "case 5\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { info->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_NON_GIANT_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { do { /* set both recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0; USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_PROC(udev->bus)->up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } Index: projects/building-blocks/sys/dev/xen/timer/timer.c =================================================================== --- projects/building-blocks/sys/dev/xen/timer/timer.c (revision 278195) +++ projects/building-blocks/sys/dev/xen/timer/timer.c (revision 278196) @@ -1,649 +1,535 @@ /*- * Copyright (c) 2009 Adrian Chadd * Copyright (c) 2012 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. * 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. * */ /** * \file dev/xen/timer/timer.c * \brief A timer driver for the Xen hypervisor's PV clock. */ #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 "clock_if.h" static devclass_t xentimer_devclass; #define NSEC_IN_SEC 1000000000ULL #define NSEC_IN_USEC 1000ULL /* 18446744073 = int(2^64 / NSEC_IN_SC) = 1 ns in 64-bit fractions */ #define FRAC_IN_NSEC 18446744073LL /* Xen timers may fire up to 100us off */ #define XENTIMER_MIN_PERIOD_IN_NSEC 100*NSEC_IN_USEC #define XENCLOCK_RESOLUTION 10000000 #define ETIME 62 /* Xen "bad time" error */ #define XENTIMER_QUALITY 950 struct xentimer_pcpu_data { uint64_t timer; uint64_t last_processed; void *irq_handle; }; DPCPU_DEFINE(struct xentimer_pcpu_data, xentimer_pcpu); DPCPU_DECLARE(struct vcpu_info *, vcpu_info); struct xentimer_softc { device_t dev; struct timecounter tc; struct eventtimer et; }; -/* Last time; this guarantees a monotonically increasing clock. */ -volatile uint64_t xen_timer_last_time = 0; - static void xentimer_identify(driver_t *driver, device_t parent) { if (!xen_domain()) return; /* Handle all Xen PV timers in one device instance. */ if (devclass_get_device(xentimer_devclass, 0)) return; BUS_ADD_CHILD(parent, 0, "xen_et", 0); } static int xentimer_probe(device_t dev) { KASSERT((xen_domain()), ("Trying to use Xen timer on bare metal")); /* * In order to attach, this driver requires the following: * - Vector callback support by the hypervisor, in order to deliver * timer interrupts to the correct CPU for CPUs other than 0. * - Access to the hypervisor shared info page, in order to look up * each VCPU's timer information and the Xen wallclock time. * - The hypervisor must say its PV clock is "safe" to use. * - The hypervisor must support VCPUOP hypercalls. * - The maximum number of CPUs supported by FreeBSD must not exceed * the number of VCPUs supported by the hypervisor. */ #define XTREQUIRES(condition, reason...) \ if (!(condition)) { \ device_printf(dev, ## reason); \ device_detach(dev); \ return (ENXIO); \ } if (xen_hvm_domain()) { XTREQUIRES(xen_vector_callback_enabled, "vector callbacks unavailable\n"); XTREQUIRES(xen_feature(XENFEAT_hvm_safe_pvclock), "HVM safe pvclock unavailable\n"); } XTREQUIRES(HYPERVISOR_shared_info != NULL, "shared info page unavailable\n"); XTREQUIRES(HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, 0, NULL) == 0, "VCPUOPs interface unavailable\n"); #undef XTREQUIRES device_set_desc(dev, "Xen PV Clock"); return (BUS_PROBE_NOWILDCARD); } -/* - * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, - * yielding a 64-bit result. - */ -static inline uint64_t -scale_delta(uint64_t delta, uint32_t mul_frac, int shift) -{ - uint64_t product; - - if (shift < 0) - delta >>= -shift; - else - delta <<= shift; - -#if defined(__i386__) - { - uint32_t tmp1, tmp2; - - /** - * For i386, the formula looks like: - * - * lower = (mul_frac * (delta & UINT_MAX)) >> 32 - * upper = mul_frac * (delta >> 32) - * product = lower + upper - */ - __asm__ ( - "mul %5 ; " - "mov %4,%%eax ; " - "mov %%edx,%4 ; " - "mul %5 ; " - "xor %5,%5 ; " - "add %4,%%eax ; " - "adc %5,%%edx ; " - : "=A" (product), "=r" (tmp1), "=r" (tmp2) - : "a" ((uint32_t)delta), "1" ((uint32_t)(delta >> 32)), - "2" (mul_frac) ); - } -#elif defined(__amd64__) - { - unsigned long tmp; - - __asm__ ( - "mulq %[mul_frac] ; shrd $32, %[hi], %[lo]" - : [lo]"=a" (product), [hi]"=d" (tmp) - : "0" (delta), [mul_frac]"rm"((uint64_t)mul_frac)); - } -#else -#error "xentimer: unsupported architecture" -#endif - - return (product); -} - -static uint64_t -get_nsec_offset(struct vcpu_time_info *tinfo) -{ - - return (scale_delta(rdtsc() - tinfo->tsc_timestamp, - tinfo->tsc_to_system_mul, tinfo->tsc_shift)); -} - -/* - * Read the current hypervisor system uptime value from Xen. - * See for a description of how this works. - */ -static uint32_t -xen_fetch_vcpu_tinfo(struct vcpu_time_info *dst, struct vcpu_time_info *src) -{ - - do { - dst->version = src->version; - rmb(); - dst->tsc_timestamp = src->tsc_timestamp; - dst->system_time = src->system_time; - dst->tsc_to_system_mul = src->tsc_to_system_mul; - dst->tsc_shift = src->tsc_shift; - rmb(); - } while ((src->version & 1) | (dst->version ^ src->version)); - - return (dst->version); -} - /** * \brief Get the current time, in nanoseconds, since the hypervisor booted. * * \param vcpu vcpu_info structure to fetch the time from. * - * \note This function returns the current CPU's idea of this value, unless - * it happens to be less than another CPU's previously determined value. */ static uint64_t xen_fetch_vcpu_time(struct vcpu_info *vcpu) { - struct vcpu_time_info dst; - struct vcpu_time_info *src; - uint32_t pre_version; - uint64_t now; - volatile uint64_t last; + struct pvclock_vcpu_time_info *time; - src = &vcpu->time; + time = (struct pvclock_vcpu_time_info *) &vcpu->time; - do { - pre_version = xen_fetch_vcpu_tinfo(&dst, src); - barrier(); - now = dst.system_time + get_nsec_offset(&dst); - barrier(); - } while (pre_version != src->version); - - /* - * Enforce a monotonically increasing clock time across all - * VCPUs. If our time is too old, use the last time and return. - * Otherwise, try to update the last time. - */ - do { - last = xen_timer_last_time; - if (last > now) { - now = last; - break; - } - } while (!atomic_cmpset_64(&xen_timer_last_time, last, now)); - - return (now); + return (pvclock_get_timecount(time)); } static uint32_t xentimer_get_timecount(struct timecounter *tc) { uint64_t vcpu_time; /* * We don't disable preemption here because the worst that can * happen is reading the vcpu_info area of a different CPU than * the one we are currently running on, but that would also * return a valid tc (and we avoid the overhead of * critical_{enter/exit} calls). */ vcpu_time = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); return (vcpu_time & UINT32_MAX); } /** * \brief Fetch the hypervisor boot time, known as the "Xen wallclock". * * \param ts Timespec to store the current stable value. * \param version Pointer to store the corresponding wallclock version. * * \note This value is updated when Domain-0 shifts its clock to follow * clock drift, e.g. as detected by NTP. */ static void xen_fetch_wallclock(struct timespec *ts) { shared_info_t *src = HYPERVISOR_shared_info; - uint32_t version = 0; + struct pvclock_wall_clock *wc; - do { - version = src->wc_version; - rmb(); - ts->tv_sec = src->wc_sec; - ts->tv_nsec = src->wc_nsec; - rmb(); - } while ((src->wc_version & 1) | (version ^ src->wc_version)); + wc = (struct pvclock_wall_clock *) &src->wc_version; + + pvclock_get_wallclock(wc, ts); } static void xen_fetch_uptime(struct timespec *ts) { uint64_t uptime; uptime = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); ts->tv_sec = uptime / NSEC_IN_SEC; ts->tv_nsec = uptime % NSEC_IN_SEC; } static int xentimer_settime(device_t dev __unused, struct timespec *ts) { /* * Don't return EINVAL here; just silently fail if the domain isn't * privileged enough to set the TOD. */ return (0); } /** * \brief Return current time according to the Xen Hypervisor wallclock. * * \param dev Xentimer device. * \param ts Pointer to store the wallclock time. * * \note The Xen time structures document the hypervisor start time and the * uptime-since-hypervisor-start (in nsec.) They need to be combined * in order to calculate a TOD clock. */ static int xentimer_gettime(device_t dev, struct timespec *ts) { struct timespec u_ts; timespecclear(ts); xen_fetch_wallclock(ts); xen_fetch_uptime(&u_ts); timespecadd(ts, &u_ts); return (0); } /** * \brief Handle a timer interrupt for the Xen PV timer driver. * * \param arg Xen timer driver softc that is expecting the interrupt. */ static int xentimer_intr(void *arg) { struct xentimer_softc *sc = (struct xentimer_softc *)arg; struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); pcpu->last_processed = xen_fetch_vcpu_time(DPCPU_GET(vcpu_info)); if (pcpu->timer != 0 && sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } static int xentimer_vcpu_start_timer(int vcpu, uint64_t next_time) { struct vcpu_set_singleshot_timer single; single.timeout_abs_ns = next_time; single.flags = VCPU_SSHOTTMR_future; return (HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, vcpu, &single)); } static int xentimer_vcpu_stop_timer(int vcpu) { return (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, vcpu, NULL)); } /** * \brief Set the next oneshot time for the current CPU. * * \param et Xen timer driver event timer to schedule on. * \param first Delta to the next time to schedule the interrupt for. * \param period Not used. * * \note See eventtimers(9) for more information. * \note * * \returns 0 */ static int xentimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { int error = 0, i = 0; struct xentimer_softc *sc = et->et_priv; int cpu = PCPU_GET(vcpu_id); struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); struct vcpu_info *vcpu = DPCPU_GET(vcpu_info); uint64_t first_in_ns, next_time; #ifdef INVARIANTS struct thread *td = curthread; #endif KASSERT(td->td_critnest != 0, ("xentimer_et_start called without preemption disabled")); /* See sbttots() for this formula. */ first_in_ns = (((first >> 32) * NSEC_IN_SEC) + (((uint64_t)NSEC_IN_SEC * (uint32_t)first) >> 32)); /* * Retry any timer scheduling failures, where the hypervisor * returns -ETIME. Sometimes even a 100us timer period isn't large * enough, but larger period instances are relatively uncommon. * * XXX Remove the panics once et_start() and its consumers are * equipped to deal with start failures. */ do { if (++i == 60) panic("can't schedule timer"); next_time = xen_fetch_vcpu_time(vcpu) + first_in_ns; error = xentimer_vcpu_start_timer(cpu, next_time); } while (error == -ETIME); if (error) panic("%s: Error %d setting singleshot timer to %"PRIu64"\n", device_get_nameunit(sc->dev), error, next_time); pcpu->timer = next_time; return (error); } /** * \brief Cancel the event timer's currently running timer, if any. */ static int xentimer_et_stop(struct eventtimer *et) { int cpu = PCPU_GET(vcpu_id); struct xentimer_pcpu_data *pcpu = DPCPU_PTR(xentimer_pcpu); pcpu->timer = 0; return (xentimer_vcpu_stop_timer(cpu)); } /** * \brief Attach a Xen PV timer driver instance. * * \param dev Bus device object to attach. * * \note * \returns EINVAL */ static int xentimer_attach(device_t dev) { struct xentimer_softc *sc = device_get_softc(dev); int error, i; sc->dev = dev; /* Bind an event channel to a VIRQ on each VCPU. */ CPU_FOREACH(i) { struct xentimer_pcpu_data *pcpu; pcpu = DPCPU_ID_PTR(i, xentimer_pcpu); error = HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, i, NULL); if (error) { device_printf(dev, "Error disabling Xen periodic timer " "on CPU %d\n", i); return (error); } error = xen_intr_bind_virq(dev, VIRQ_TIMER, i, xentimer_intr, NULL, sc, INTR_TYPE_CLK, &pcpu->irq_handle); if (error) { device_printf(dev, "Error %d binding VIRQ_TIMER " "to VCPU %d\n", error, i); return (error); } xen_intr_describe(pcpu->irq_handle, "c%d", i); } /* Register the event timer. */ sc->et.et_name = "XENTIMER"; sc->et.et_quality = XENTIMER_QUALITY; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; sc->et.et_frequency = NSEC_IN_SEC; /* See tstosbt() for this formula */ sc->et.et_min_period = (XENTIMER_MIN_PERIOD_IN_NSEC * (((uint64_t)1 << 63) / 500000000) >> 32); sc->et.et_max_period = ((sbintime_t)4 << 32); sc->et.et_start = xentimer_et_start; sc->et.et_stop = xentimer_et_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Register the timecounter. */ sc->tc.tc_name = "XENTIMER"; sc->tc.tc_quality = XENTIMER_QUALITY; sc->tc.tc_flags = TC_FLAGS_SUSPEND_SAFE; /* * The underlying resolution is in nanoseconds, since the timer info * scales TSC frequencies using a fraction that represents time in * terms of nanoseconds. */ sc->tc.tc_frequency = NSEC_IN_SEC; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_get_timecount = xentimer_get_timecount; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* Register the Hypervisor wall clock */ clock_register(dev, XENCLOCK_RESOLUTION); return (0); } static int xentimer_detach(device_t dev) { /* Implement Xen PV clock teardown - XXX see hpet_detach ? */ /* If possible: * 1. need to deregister timecounter * 2. need to deregister event timer * 3. need to deregister virtual IRQ event channels */ return (EBUSY); } static void xentimer_percpu_resume(void *arg) { device_t dev = (device_t) arg; struct xentimer_softc *sc = device_get_softc(dev); xentimer_et_start(&sc->et, sc->et.et_min_period, 0); } static int xentimer_resume(device_t dev) { int error; int i; /* Disable the periodic timer */ CPU_FOREACH(i) { error = HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, i, NULL); if (error != 0) { device_printf(dev, "Error disabling Xen periodic timer on CPU %d\n", i); return (error); } } /* Reset the last uptime value */ - xen_timer_last_time = 0; + pvclock_resume(); /* Reset the RTC clock */ inittodr(time_second); /* Kick the timers on all CPUs */ smp_rendezvous(NULL, xentimer_percpu_resume, NULL, dev); if (bootverbose) device_printf(dev, "resumed operation after suspension\n"); return (0); } static int xentimer_suspend(device_t dev) { return (0); } /* * Xen early clock init */ void xen_clock_init(void) { } /* * Xen PV DELAY function * * When running on PVH mode we don't have an emulated i8524, so * make use of the Xen time info in order to code a simple DELAY * function that can be used during early boot. */ void xen_delay(int n) { struct vcpu_info *vcpu = &HYPERVISOR_shared_info->vcpu_info[0]; uint64_t end_ns; uint64_t current; end_ns = xen_fetch_vcpu_time(vcpu); end_ns += n * NSEC_IN_USEC; for (;;) { current = xen_fetch_vcpu_time(vcpu); if (current >= end_ns) break; } } static device_method_t xentimer_methods[] = { DEVMETHOD(device_identify, xentimer_identify), DEVMETHOD(device_probe, xentimer_probe), DEVMETHOD(device_attach, xentimer_attach), DEVMETHOD(device_detach, xentimer_detach), DEVMETHOD(device_suspend, xentimer_suspend), DEVMETHOD(device_resume, xentimer_resume), /* clock interface */ DEVMETHOD(clock_gettime, xentimer_gettime), DEVMETHOD(clock_settime, xentimer_settime), DEVMETHOD_END }; static driver_t xentimer_driver = { "xen_et", xentimer_methods, sizeof(struct xentimer_softc), }; DRIVER_MODULE(xentimer, xenpv, xentimer_driver, xentimer_devclass, 0, 0); MODULE_DEPEND(xentimer, xenpv, 1, 1, 1); Index: projects/building-blocks/sys/fs/tmpfs/tmpfs_subr.c =================================================================== --- projects/building-blocks/sys/fs/tmpfs/tmpfs_subr.c (revision 278195) +++ projects/building-blocks/sys/fs/tmpfs/tmpfs_subr.c (revision 278196) @@ -1,1814 +1,1813 @@ /* $NetBSD: tmpfs_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Julio M. Merino Vidal, developed as part of Google's Summer of Code * 2005 program. * * 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. */ /* * Efficient memory file system supporting functions. */ #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 struct tmpfs_dir_cursor { struct tmpfs_dirent *tdc_current; struct tmpfs_dirent *tdc_tree; }; SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "tmpfs file system"); static long tmpfs_pages_reserved = TMPFS_PAGES_MINRESERVED; static int sysctl_mem_reserved(SYSCTL_HANDLER_ARGS) { int error; long pages, bytes; pages = *(long *)arg1; bytes = pages * PAGE_SIZE; error = sysctl_handle_long(oidp, &bytes, 0, req); if (error || !req->newptr) return (error); pages = bytes / PAGE_SIZE; if (pages < TMPFS_PAGES_MINRESERVED) return (EINVAL); *(long *)arg1 = pages; return (0); } SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_reserved, CTLTYPE_LONG|CTLFLAG_RW, &tmpfs_pages_reserved, 0, sysctl_mem_reserved, "L", "Amount of available memory and swap below which tmpfs growth stops"); static __inline int tmpfs_dirtree_cmp(struct tmpfs_dirent *a, struct tmpfs_dirent *b); RB_PROTOTYPE_STATIC(tmpfs_dir, tmpfs_dirent, uh.td_entries, tmpfs_dirtree_cmp); size_t tmpfs_mem_avail(void) { vm_ooffset_t avail; avail = swap_pager_avail + vm_cnt.v_free_count + vm_cnt.v_cache_count - tmpfs_pages_reserved; if (__predict_false(avail < 0)) avail = 0; return (avail); } size_t tmpfs_pages_used(struct tmpfs_mount *tmp) { const size_t node_size = sizeof(struct tmpfs_node) + sizeof(struct tmpfs_dirent); size_t meta_pages; meta_pages = howmany((uintmax_t)tmp->tm_nodes_inuse * node_size, PAGE_SIZE); return (meta_pages + tmp->tm_pages_used); } static size_t tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages) { if (tmpfs_mem_avail() < req_pages) return (0); if (tmp->tm_pages_max != SIZE_MAX && tmp->tm_pages_max < req_pages + tmpfs_pages_used(tmp)) return (0); return (1); } /* * Allocates a new node of type 'type' inside the 'tmp' mount point, with * its owner set to 'uid', its group to 'gid' and its mode set to 'mode', * using the credentials of the process 'p'. * * If the node type is set to 'VDIR', then the parent parameter must point * to the parent directory of the node being created. It may only be NULL * while allocating the root node. * * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter * specifies the device the node represents. * * If the node type is set to 'VLNK', then the parameter target specifies * the file name of the target file for the symbolic link that is being * created. * * Note that new nodes are retrieved from the available list if it has * items or, if it is empty, from the node pool as long as there is enough * space to create them. * * Returns zero on success or an appropriate error code on failure. */ int tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, char *target, dev_t rdev, struct tmpfs_node **node) { struct tmpfs_node *nnode; vm_object_t obj; /* If the root directory of the 'tmp' file system is not yet * allocated, this must be the request to do it. */ MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); KASSERT(tmp->tm_root == NULL || mp->mnt_writeopcount > 0, ("creating node not under vn_start_write")); MPASS(IFF(type == VLNK, target != NULL)); MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max) return (ENOSPC); if (tmpfs_pages_check_avail(tmp, 1) == 0) return (ENOSPC); if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0) { /* * When a new tmpfs node is created for fully * constructed mount point, there must be a parent * node, which vnode is locked exclusively. As * consequence, if the unmount is executing in * parallel, vflush() cannot reclaim the parent vnode. * Due to this, the check for MNTK_UNMOUNT flag is not * racy: if we did not see MNTK_UNMOUNT flag, then tmp * cannot be destroyed until node construction is * finished and the parent vnode unlocked. * * Tmpfs does not need to instantiate new nodes during * unmount. */ return (EBUSY); } nnode = (struct tmpfs_node *)uma_zalloc_arg( tmp->tm_node_pool, tmp, M_WAITOK); /* Generic initialization. */ nnode->tn_type = type; vfs_timestamp(&nnode->tn_atime); nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime = nnode->tn_atime; nnode->tn_uid = uid; nnode->tn_gid = gid; nnode->tn_mode = mode; nnode->tn_id = alloc_unr(tmp->tm_ino_unr); /* Type-specific initialization. */ switch (nnode->tn_type) { case VBLK: case VCHR: nnode->tn_rdev = rdev; break; case VDIR: RB_INIT(&nnode->tn_dir.tn_dirhead); LIST_INIT(&nnode->tn_dir.tn_dupindex); MPASS(parent != nnode); MPASS(IMPLIES(parent == NULL, tmp->tm_root == NULL)); nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent; nnode->tn_dir.tn_readdir_lastn = 0; nnode->tn_dir.tn_readdir_lastp = NULL; nnode->tn_links++; TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent); nnode->tn_dir.tn_parent->tn_links++; TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent); break; case VFIFO: /* FALLTHROUGH */ case VSOCK: break; case VLNK: MPASS(strlen(target) < MAXPATHLEN); nnode->tn_size = strlen(target); nnode->tn_link = malloc(nnode->tn_size, M_TMPFSNAME, M_WAITOK); memcpy(nnode->tn_link, target, nnode->tn_size); break; case VREG: obj = nnode->tn_reg.tn_aobj = vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0, NULL /* XXXKIB - tmpfs needs swap reservation */); VM_OBJECT_WLOCK(obj); /* OBJ_TMPFS is set together with the setting of vp->v_object */ vm_object_set_flag(obj, OBJ_NOSPLIT | OBJ_TMPFS_NODE); vm_object_clear_flag(obj, OBJ_ONEMAPPING); VM_OBJECT_WUNLOCK(obj); break; default: panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type); } TMPFS_LOCK(tmp); LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries); tmp->tm_nodes_inuse++; TMPFS_UNLOCK(tmp); *node = nnode; return 0; } /* * Destroys the node pointed to by node from the file system 'tmp'. * If the node does not belong to the given mount point, the results are * unpredicted. * * If the node references a directory; no entries are allowed because * their removal could need a recursive algorithm, something forbidden in * kernel space. Furthermore, there is not need to provide such * functionality (recursive removal) because the only primitives offered * to the user are the removal of empty directories and the deletion of * individual files. * * Note that nodes are not really deleted; in fact, when a node has been * allocated, it cannot be deleted during the whole life of the file * system. Instead, they are moved to the available list and remain there * until reused. */ void tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node) { vm_object_t uobj; #ifdef INVARIANTS TMPFS_NODE_LOCK(node); MPASS(node->tn_vnode == NULL); MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0); TMPFS_NODE_UNLOCK(node); #endif TMPFS_LOCK(tmp); LIST_REMOVE(node, tn_entries); tmp->tm_nodes_inuse--; TMPFS_UNLOCK(tmp); switch (node->tn_type) { case VNON: /* Do not do anything. VNON is provided to let the * allocation routine clean itself easily by avoiding * duplicating code in it. */ /* FALLTHROUGH */ case VBLK: /* FALLTHROUGH */ case VCHR: /* FALLTHROUGH */ case VDIR: /* FALLTHROUGH */ case VFIFO: /* FALLTHROUGH */ case VSOCK: break; case VLNK: free(node->tn_link, M_TMPFSNAME); break; case VREG: uobj = node->tn_reg.tn_aobj; if (uobj != NULL) { TMPFS_LOCK(tmp); tmp->tm_pages_used -= uobj->size; TMPFS_UNLOCK(tmp); KASSERT((uobj->flags & OBJ_TMPFS) == 0, ("leaked OBJ_TMPFS node %p vm_obj %p", node, uobj)); vm_object_deallocate(uobj); } break; default: panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type); } free_unr(tmp->tm_ino_unr, node->tn_id); uma_zfree(tmp->tm_node_pool, node); } static __inline uint32_t tmpfs_dirent_hash(const char *name, u_int len) { uint32_t hash; hash = fnv_32_buf(name, len, FNV1_32_INIT + len) & TMPFS_DIRCOOKIE_MASK; #ifdef TMPFS_DEBUG_DIRCOOKIE_DUP hash &= 0xf; #endif if (hash < TMPFS_DIRCOOKIE_MIN) hash += TMPFS_DIRCOOKIE_MIN; return (hash); } static __inline off_t tmpfs_dirent_cookie(struct tmpfs_dirent *de) { if (de == NULL) return (TMPFS_DIRCOOKIE_EOF); MPASS(de->td_cookie >= TMPFS_DIRCOOKIE_MIN); return (de->td_cookie); } static __inline boolean_t tmpfs_dirent_dup(struct tmpfs_dirent *de) { return ((de->td_cookie & TMPFS_DIRCOOKIE_DUP) != 0); } static __inline boolean_t tmpfs_dirent_duphead(struct tmpfs_dirent *de) { return ((de->td_cookie & TMPFS_DIRCOOKIE_DUPHEAD) != 0); } void tmpfs_dirent_init(struct tmpfs_dirent *de, const char *name, u_int namelen) { de->td_hash = de->td_cookie = tmpfs_dirent_hash(name, namelen); memcpy(de->ud.td_name, name, namelen); de->td_namelen = namelen; } /* * Allocates a new directory entry for the node node with a name of name. * The new directory entry is returned in *de. * * The link count of node is increased by one to reflect the new object * referencing it. * * Returns zero on success or an appropriate error code on failure. */ int tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node, const char *name, u_int len, struct tmpfs_dirent **de) { struct tmpfs_dirent *nde; nde = uma_zalloc(tmp->tm_dirent_pool, M_WAITOK); nde->td_node = node; if (name != NULL) { nde->ud.td_name = malloc(len, M_TMPFSNAME, M_WAITOK); tmpfs_dirent_init(nde, name, len); } else nde->td_namelen = 0; if (node != NULL) node->tn_links++; *de = nde; return 0; } /* * Frees a directory entry. It is the caller's responsibility to destroy * the node referenced by it if needed. * * The link count of node is decreased by one to reflect the removal of an * object that referenced it. This only happens if 'node_exists' is true; * otherwise the function will not access the node referred to by the * directory entry, as it may already have been released from the outside. */ void tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de) { struct tmpfs_node *node; node = de->td_node; if (node != NULL) { MPASS(node->tn_links > 0); node->tn_links--; } if (!tmpfs_dirent_duphead(de) && de->ud.td_name != NULL) free(de->ud.td_name, M_TMPFSNAME); uma_zfree(tmp->tm_dirent_pool, de); } void tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj) { ASSERT_VOP_ELOCKED(vp, "tmpfs_destroy_vobject"); if (vp->v_type != VREG || obj == NULL) return; VM_OBJECT_WLOCK(obj); VI_LOCK(vp); vm_object_clear_flag(obj, OBJ_TMPFS); obj->un_pager.swp.swp_tmpfs = NULL; VI_UNLOCK(vp); VM_OBJECT_WUNLOCK(obj); } /* * Need to clear v_object for insmntque failure. */ static void tmpfs_insmntque_dtr(struct vnode *vp, void *dtr_arg) { tmpfs_destroy_vobject(vp, vp->v_object); vp->v_object = NULL; vp->v_data = NULL; vp->v_op = &dead_vnodeops; vgone(vp); vput(vp); } /* * Allocates a new vnode for the node node or returns a new reference to * an existing one if the node had already a vnode referencing it. The * resulting locked vnode is returned in *vpp. * * Returns zero on success or an appropriate error code on failure. */ int tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag, struct vnode **vpp) { struct vnode *vp; vm_object_t object; int error; error = 0; loop: TMPFS_NODE_LOCK(node); loop1: if ((vp = node->tn_vnode) != NULL) { MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0); VI_LOCK(vp); if ((node->tn_type == VDIR && node->tn_dir.tn_parent == NULL) || ((vp->v_iflag & VI_DOOMED) != 0 && (lkflag & LK_NOWAIT) != 0)) { VI_UNLOCK(vp); TMPFS_NODE_UNLOCK(node); error = ENOENT; vp = NULL; goto out; } if ((vp->v_iflag & VI_DOOMED) != 0) { VI_UNLOCK(vp); node->tn_vpstate |= TMPFS_VNODE_WRECLAIM; while ((node->tn_vpstate & TMPFS_VNODE_WRECLAIM) != 0) { msleep(&node->tn_vnode, TMPFS_NODE_MTX(node), 0, "tmpfsE", 0); } goto loop1; } TMPFS_NODE_UNLOCK(node); error = vget(vp, lkflag | LK_INTERLOCK, curthread); if (error == ENOENT) goto loop; if (error != 0) { vp = NULL; goto out; } /* * Make sure the vnode is still there after * getting the interlock to avoid racing a free. */ if (node->tn_vnode == NULL || node->tn_vnode != vp) { vput(vp); goto loop; } goto out; } if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) || (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) { TMPFS_NODE_UNLOCK(node); error = ENOENT; vp = NULL; goto out; } /* * otherwise lock the vp list while we call getnewvnode * since that can block. */ if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) { node->tn_vpstate |= TMPFS_VNODE_WANT; error = msleep((caddr_t) &node->tn_vpstate, TMPFS_NODE_MTX(node), PDROP | PCATCH, "tmpfs_alloc_vp", 0); if (error) return error; goto loop; } else node->tn_vpstate |= TMPFS_VNODE_ALLOCATING; TMPFS_NODE_UNLOCK(node); /* Get a new vnode and associate it with our node. */ error = getnewvnode("tmpfs", mp, &tmpfs_vnodeop_entries, &vp); if (error != 0) goto unlock; MPASS(vp != NULL); /* lkflag is ignored, the lock is exclusive */ (void) vn_lock(vp, lkflag | LK_RETRY); vp->v_data = node; vp->v_type = node->tn_type; /* Type-specific initialization. */ switch (node->tn_type) { case VBLK: /* FALLTHROUGH */ case VCHR: /* FALLTHROUGH */ case VLNK: /* FALLTHROUGH */ case VSOCK: break; case VFIFO: vp->v_op = &tmpfs_fifoop_entries; break; case VREG: object = node->tn_reg.tn_aobj; VM_OBJECT_WLOCK(object); VI_LOCK(vp); KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs")); vp->v_object = object; object->un_pager.swp.swp_tmpfs = vp; vm_object_set_flag(object, OBJ_TMPFS); VI_UNLOCK(vp); VM_OBJECT_WUNLOCK(object); break; case VDIR: MPASS(node->tn_dir.tn_parent != NULL); if (node->tn_dir.tn_parent == node) vp->v_vflag |= VV_ROOT; break; default: panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type); } if (vp->v_type != VFIFO) VN_LOCK_ASHARE(vp); error = insmntque1(vp, mp, tmpfs_insmntque_dtr, NULL); if (error) vp = NULL; unlock: TMPFS_NODE_LOCK(node); MPASS(node->tn_vpstate & TMPFS_VNODE_ALLOCATING); node->tn_vpstate &= ~TMPFS_VNODE_ALLOCATING; node->tn_vnode = vp; if (node->tn_vpstate & TMPFS_VNODE_WANT) { node->tn_vpstate &= ~TMPFS_VNODE_WANT; TMPFS_NODE_UNLOCK(node); wakeup((caddr_t) &node->tn_vpstate); } else TMPFS_NODE_UNLOCK(node); out: *vpp = vp; #ifdef INVARIANTS if (error == 0) { MPASS(*vpp != NULL && VOP_ISLOCKED(*vpp)); TMPFS_NODE_LOCK(node); MPASS(*vpp == node->tn_vnode); TMPFS_NODE_UNLOCK(node); } #endif return error; } /* * Destroys the association between the vnode vp and the node it * references. */ void tmpfs_free_vp(struct vnode *vp) { struct tmpfs_node *node; node = VP_TO_TMPFS_NODE(vp); TMPFS_NODE_ASSERT_LOCKED(node); node->tn_vnode = NULL; if ((node->tn_vpstate & TMPFS_VNODE_WRECLAIM) != 0) wakeup(&node->tn_vnode); node->tn_vpstate &= ~TMPFS_VNODE_WRECLAIM; vp->v_data = NULL; } /* * Allocates a new file of type 'type' and adds it to the parent directory * 'dvp'; this addition is done using the component name given in 'cnp'. * The ownership of the new file is automatically assigned based on the * credentials of the caller (through 'cnp'), the group is set based on * the parent directory and the mode is determined from the 'vap' argument. * If successful, *vpp holds a vnode to the newly created file and zero * is returned. Otherwise *vpp is NULL and the function returns an * appropriate error code. */ int tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, struct componentname *cnp, char *target) { int error; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; struct tmpfs_node *parent; MPASS(VOP_ISLOCKED(dvp)); MPASS(cnp->cn_flags & HASBUF); tmp = VFS_TO_TMPFS(dvp->v_mount); dnode = VP_TO_TMPFS_DIR(dvp); *vpp = NULL; /* If the entry we are creating is a directory, we cannot overflow * the number of links of its parent, because it will get a new * link. */ if (vap->va_type == VDIR) { /* Ensure that we do not overflow the maximum number of links * imposed by the system. */ MPASS(dnode->tn_links <= LINK_MAX); if (dnode->tn_links == LINK_MAX) { return (EMLINK); } parent = dnode; MPASS(parent != NULL); } else parent = NULL; /* Allocate a node that represents the new file. */ error = tmpfs_alloc_node(dvp->v_mount, tmp, vap->va_type, cnp->cn_cred->cr_uid, dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node); if (error != 0) return (error); /* Allocate a directory entry that points to the new file. */ error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen, &de); if (error != 0) { tmpfs_free_node(tmp, node); return (error); } /* Allocate a vnode for the new file. */ error = tmpfs_alloc_vp(dvp->v_mount, node, LK_EXCLUSIVE, vpp); if (error != 0) { tmpfs_free_dirent(tmp, de); tmpfs_free_node(tmp, node); return (error); } /* Now that all required items are allocated, we can proceed to * insert the new node into the directory, an operation that * cannot fail. */ if (cnp->cn_flags & ISWHITEOUT) tmpfs_dir_whiteout_remove(dvp, cnp); tmpfs_dir_attach(dvp, de); return (0); } static struct tmpfs_dirent * tmpfs_dir_first(struct tmpfs_node *dnode, struct tmpfs_dir_cursor *dc) { struct tmpfs_dirent *de; de = RB_MIN(tmpfs_dir, &dnode->tn_dir.tn_dirhead); dc->tdc_tree = de; if (de != NULL && tmpfs_dirent_duphead(de)) de = LIST_FIRST(&de->ud.td_duphead); dc->tdc_current = de; return (dc->tdc_current); } static struct tmpfs_dirent * tmpfs_dir_next(struct tmpfs_node *dnode, struct tmpfs_dir_cursor *dc) { struct tmpfs_dirent *de; MPASS(dc->tdc_tree != NULL); if (tmpfs_dirent_dup(dc->tdc_current)) { dc->tdc_current = LIST_NEXT(dc->tdc_current, uh.td_dup.entries); if (dc->tdc_current != NULL) return (dc->tdc_current); } dc->tdc_tree = dc->tdc_current = RB_NEXT(tmpfs_dir, &dnode->tn_dir.tn_dirhead, dc->tdc_tree); if ((de = dc->tdc_current) != NULL && tmpfs_dirent_duphead(de)) { dc->tdc_current = LIST_FIRST(&de->ud.td_duphead); MPASS(dc->tdc_current != NULL); } return (dc->tdc_current); } /* Lookup directory entry in RB-Tree. Function may return duphead entry. */ static struct tmpfs_dirent * tmpfs_dir_xlookup_hash(struct tmpfs_node *dnode, uint32_t hash) { struct tmpfs_dirent *de, dekey; dekey.td_hash = hash; de = RB_FIND(tmpfs_dir, &dnode->tn_dir.tn_dirhead, &dekey); return (de); } /* Lookup directory entry by cookie, initialize directory cursor accordingly. */ static struct tmpfs_dirent * tmpfs_dir_lookup_cookie(struct tmpfs_node *node, off_t cookie, struct tmpfs_dir_cursor *dc) { struct tmpfs_dir *dirhead = &node->tn_dir.tn_dirhead; struct tmpfs_dirent *de, dekey; MPASS(cookie >= TMPFS_DIRCOOKIE_MIN); if (cookie == node->tn_dir.tn_readdir_lastn && (de = node->tn_dir.tn_readdir_lastp) != NULL) { /* Protect against possible race, tn_readdir_last[pn] * may be updated with only shared vnode lock held. */ if (cookie == tmpfs_dirent_cookie(de)) goto out; } if ((cookie & TMPFS_DIRCOOKIE_DUP) != 0) { LIST_FOREACH(de, &node->tn_dir.tn_dupindex, uh.td_dup.index_entries) { MPASS(tmpfs_dirent_dup(de)); if (de->td_cookie == cookie) goto out; /* dupindex list is sorted. */ if (de->td_cookie < cookie) { de = NULL; goto out; } } MPASS(de == NULL); goto out; } MPASS((cookie & TMPFS_DIRCOOKIE_MASK) == cookie); dekey.td_hash = cookie; /* Recover if direntry for cookie was removed */ de = RB_NFIND(tmpfs_dir, dirhead, &dekey); dc->tdc_tree = de; dc->tdc_current = de; if (de != NULL && tmpfs_dirent_duphead(de)) { dc->tdc_current = LIST_FIRST(&de->ud.td_duphead); MPASS(dc->tdc_current != NULL); } return (dc->tdc_current); out: dc->tdc_tree = de; dc->tdc_current = de; if (de != NULL && tmpfs_dirent_dup(de)) dc->tdc_tree = tmpfs_dir_xlookup_hash(node, de->td_hash); return (dc->tdc_current); } /* * Looks for a directory entry in the directory represented by node. * 'cnp' describes the name of the entry to look for. Note that the . * and .. components are not allowed as they do not physically exist * within directories. * * Returns a pointer to the entry when found, otherwise NULL. */ struct tmpfs_dirent * tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f, struct componentname *cnp) { struct tmpfs_dir_duphead *duphead; struct tmpfs_dirent *de; uint32_t hash; MPASS(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.')); MPASS(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' && cnp->cn_nameptr[1] == '.'))); TMPFS_VALIDATE_DIR(node); hash = tmpfs_dirent_hash(cnp->cn_nameptr, cnp->cn_namelen); de = tmpfs_dir_xlookup_hash(node, hash); if (de != NULL && tmpfs_dirent_duphead(de)) { duphead = &de->ud.td_duphead; LIST_FOREACH(de, duphead, uh.td_dup.entries) { if (TMPFS_DIRENT_MATCHES(de, cnp->cn_nameptr, cnp->cn_namelen)) break; } } else if (de != NULL) { if (!TMPFS_DIRENT_MATCHES(de, cnp->cn_nameptr, cnp->cn_namelen)) de = NULL; } if (de != NULL && f != NULL && de->td_node != f) de = NULL; return (de); } /* * Attach duplicate-cookie directory entry nde to dnode and insert to dupindex * list, allocate new cookie value. */ static void tmpfs_dir_attach_dup(struct tmpfs_node *dnode, struct tmpfs_dir_duphead *duphead, struct tmpfs_dirent *nde) { struct tmpfs_dir_duphead *dupindex; struct tmpfs_dirent *de, *pde; dupindex = &dnode->tn_dir.tn_dupindex; de = LIST_FIRST(dupindex); if (de == NULL || de->td_cookie < TMPFS_DIRCOOKIE_DUP_MAX) { if (de == NULL) nde->td_cookie = TMPFS_DIRCOOKIE_DUP_MIN; else nde->td_cookie = de->td_cookie + 1; MPASS(tmpfs_dirent_dup(nde)); LIST_INSERT_HEAD(dupindex, nde, uh.td_dup.index_entries); LIST_INSERT_HEAD(duphead, nde, uh.td_dup.entries); return; } /* * Cookie numbers are near exhaustion. Scan dupindex list for unused * numbers. dupindex list is sorted in descending order. Keep it so * after inserting nde. */ while (1) { pde = de; de = LIST_NEXT(de, uh.td_dup.index_entries); if (de == NULL && pde->td_cookie != TMPFS_DIRCOOKIE_DUP_MIN) { /* * Last element of the index doesn't have minimal cookie * value, use it. */ nde->td_cookie = TMPFS_DIRCOOKIE_DUP_MIN; LIST_INSERT_AFTER(pde, nde, uh.td_dup.index_entries); LIST_INSERT_HEAD(duphead, nde, uh.td_dup.entries); return; } else if (de == NULL) { /* * We are so lucky have 2^30 hash duplicates in single * directory :) Return largest possible cookie value. * It should be fine except possible issues with * VOP_READDIR restart. */ nde->td_cookie = TMPFS_DIRCOOKIE_DUP_MAX; LIST_INSERT_HEAD(dupindex, nde, uh.td_dup.index_entries); LIST_INSERT_HEAD(duphead, nde, uh.td_dup.entries); return; } if (de->td_cookie + 1 == pde->td_cookie || de->td_cookie >= TMPFS_DIRCOOKIE_DUP_MAX) continue; /* No hole or invalid cookie. */ nde->td_cookie = de->td_cookie + 1; MPASS(tmpfs_dirent_dup(nde)); MPASS(pde->td_cookie > nde->td_cookie); MPASS(nde->td_cookie > de->td_cookie); LIST_INSERT_BEFORE(de, nde, uh.td_dup.index_entries); LIST_INSERT_HEAD(duphead, nde, uh.td_dup.entries); return; }; } /* * Attaches the directory entry de to the directory represented by vp. * Note that this does not change the link count of the node pointed by * the directory entry, as this is done by tmpfs_alloc_dirent. */ void tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de) { struct tmpfs_node *dnode; struct tmpfs_dirent *xde, *nde; ASSERT_VOP_ELOCKED(vp, __func__); MPASS(de->td_namelen > 0); MPASS(de->td_hash >= TMPFS_DIRCOOKIE_MIN); MPASS(de->td_cookie == de->td_hash); dnode = VP_TO_TMPFS_DIR(vp); dnode->tn_dir.tn_readdir_lastn = 0; dnode->tn_dir.tn_readdir_lastp = NULL; MPASS(!tmpfs_dirent_dup(de)); xde = RB_INSERT(tmpfs_dir, &dnode->tn_dir.tn_dirhead, de); if (xde != NULL && tmpfs_dirent_duphead(xde)) tmpfs_dir_attach_dup(dnode, &xde->ud.td_duphead, de); else if (xde != NULL) { /* * Allocate new duphead. Swap xde with duphead to avoid * adding/removing elements with the same hash. */ MPASS(!tmpfs_dirent_dup(xde)); tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), NULL, NULL, 0, &nde); /* *nde = *xde; XXX gcc 4.2.1 may generate invalid code. */ memcpy(nde, xde, sizeof(*xde)); xde->td_cookie |= TMPFS_DIRCOOKIE_DUPHEAD; LIST_INIT(&xde->ud.td_duphead); xde->td_namelen = 0; xde->td_node = NULL; tmpfs_dir_attach_dup(dnode, &xde->ud.td_duphead, nde); tmpfs_dir_attach_dup(dnode, &xde->ud.td_duphead, de); } dnode->tn_size += sizeof(struct tmpfs_dirent); dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; tmpfs_update(vp); } /* * Detaches the directory entry de from the directory represented by vp. * Note that this does not change the link count of the node pointed by * the directory entry, as this is done by tmpfs_free_dirent. */ void tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de) { struct tmpfs_mount *tmp; struct tmpfs_dir *head; struct tmpfs_node *dnode; struct tmpfs_dirent *xde; ASSERT_VOP_ELOCKED(vp, __func__); dnode = VP_TO_TMPFS_DIR(vp); head = &dnode->tn_dir.tn_dirhead; dnode->tn_dir.tn_readdir_lastn = 0; dnode->tn_dir.tn_readdir_lastp = NULL; if (tmpfs_dirent_dup(de)) { /* Remove duphead if de was last entry. */ if (LIST_NEXT(de, uh.td_dup.entries) == NULL) { xde = tmpfs_dir_xlookup_hash(dnode, de->td_hash); MPASS(tmpfs_dirent_duphead(xde)); } else xde = NULL; LIST_REMOVE(de, uh.td_dup.entries); LIST_REMOVE(de, uh.td_dup.index_entries); if (xde != NULL) { if (LIST_EMPTY(&xde->ud.td_duphead)) { RB_REMOVE(tmpfs_dir, head, xde); tmp = VFS_TO_TMPFS(vp->v_mount); MPASS(xde->td_node == NULL); tmpfs_free_dirent(tmp, xde); } } } else RB_REMOVE(tmpfs_dir, head, de); dnode->tn_size -= sizeof(struct tmpfs_dirent); dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; tmpfs_update(vp); } void tmpfs_dir_destroy(struct tmpfs_mount *tmp, struct tmpfs_node *dnode) { struct tmpfs_dirent *de, *dde, *nde; RB_FOREACH_SAFE(de, tmpfs_dir, &dnode->tn_dir.tn_dirhead, nde) { RB_REMOVE(tmpfs_dir, &dnode->tn_dir.tn_dirhead, de); /* Node may already be destroyed. */ de->td_node = NULL; if (tmpfs_dirent_duphead(de)) { while ((dde = LIST_FIRST(&de->ud.td_duphead)) != NULL) { LIST_REMOVE(dde, uh.td_dup.entries); dde->td_node = NULL; tmpfs_free_dirent(tmp, dde); } } tmpfs_free_dirent(tmp, de); } } /* * Helper function for tmpfs_readdir. Creates a '.' entry for the given * directory and returns it in the uio space. The function returns 0 * on success, -1 if there was not enough space in the uio structure to * hold the directory entry or an appropriate error code if another * error happens. */ static int tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio) { int error; struct dirent dent; TMPFS_VALIDATE_DIR(node); MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOT); dent.d_fileno = node->tn_id; dent.d_type = DT_DIR; dent.d_namlen = 1; dent.d_name[0] = '.'; dent.d_name[1] = '\0'; dent.d_reclen = GENERIC_DIRSIZ(&dent); if (dent.d_reclen > uio->uio_resid) error = EJUSTRETURN; else error = uiomove(&dent, dent.d_reclen, uio); node->tn_status |= TMPFS_NODE_ACCESSED; return error; } /* * Helper function for tmpfs_readdir. Creates a '..' entry for the given * directory and returns it in the uio space. The function returns 0 * on success, -1 if there was not enough space in the uio structure to * hold the directory entry or an appropriate error code if another * error happens. */ static int tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) { int error; struct dirent dent; TMPFS_VALIDATE_DIR(node); MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); /* * Return ENOENT if the current node is already removed. */ TMPFS_ASSERT_LOCKED(node); if (node->tn_dir.tn_parent == NULL) { return (ENOENT); } TMPFS_NODE_LOCK(node->tn_dir.tn_parent); dent.d_fileno = node->tn_dir.tn_parent->tn_id; TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent); dent.d_type = DT_DIR; dent.d_namlen = 2; dent.d_name[0] = '.'; dent.d_name[1] = '.'; dent.d_name[2] = '\0'; dent.d_reclen = GENERIC_DIRSIZ(&dent); if (dent.d_reclen > uio->uio_resid) error = EJUSTRETURN; else error = uiomove(&dent, dent.d_reclen, uio); node->tn_status |= TMPFS_NODE_ACCESSED; return error; } /* * Helper function for tmpfs_readdir. Returns as much directory entries * as can fit in the uio space. The read starts at uio->uio_offset. * The function returns 0 on success, -1 if there was not enough space * in the uio structure to hold the directory entry or an appropriate * error code if another error happens. */ int tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, int maxcookies, u_long *cookies, int *ncookies) { struct tmpfs_dir_cursor dc; struct tmpfs_dirent *de; off_t off; int error; TMPFS_VALIDATE_DIR(node); off = 0; /* * Lookup the node from the current offset. The starting offset of * 0 will lookup both '.' and '..', and then the first real entry, * or EOF if there are none. Then find all entries for the dir that * fit into the buffer. Once no more entries are found (de == NULL), * the offset is set to TMPFS_DIRCOOKIE_EOF, which will cause the next * call to return 0. */ switch (uio->uio_offset) { case TMPFS_DIRCOOKIE_DOT: error = tmpfs_dir_getdotdent(node, uio); if (error != 0) return (error); uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT; if (cookies != NULL) cookies[(*ncookies)++] = off = uio->uio_offset; /* FALLTHROUGH */ case TMPFS_DIRCOOKIE_DOTDOT: error = tmpfs_dir_getdotdotdent(node, uio); if (error != 0) return (error); de = tmpfs_dir_first(node, &dc); uio->uio_offset = tmpfs_dirent_cookie(de); if (cookies != NULL) cookies[(*ncookies)++] = off = uio->uio_offset; /* EOF. */ if (de == NULL) return (0); break; case TMPFS_DIRCOOKIE_EOF: return (0); default: de = tmpfs_dir_lookup_cookie(node, uio->uio_offset, &dc); if (de == NULL) return (EINVAL); if (cookies != NULL) off = tmpfs_dirent_cookie(de); } /* Read as much entries as possible; i.e., until we reach the end of * the directory or we exhaust uio space. */ do { struct dirent d; /* Create a dirent structure representing the current * tmpfs_node and fill it. */ if (de->td_node == NULL) { d.d_fileno = 1; d.d_type = DT_WHT; } else { d.d_fileno = de->td_node->tn_id; switch (de->td_node->tn_type) { case VBLK: d.d_type = DT_BLK; break; case VCHR: d.d_type = DT_CHR; break; case VDIR: d.d_type = DT_DIR; break; case VFIFO: d.d_type = DT_FIFO; break; case VLNK: d.d_type = DT_LNK; break; case VREG: d.d_type = DT_REG; break; case VSOCK: d.d_type = DT_SOCK; break; default: panic("tmpfs_dir_getdents: type %p %d", de->td_node, (int)de->td_node->tn_type); } } d.d_namlen = de->td_namelen; MPASS(de->td_namelen < sizeof(d.d_name)); (void)memcpy(d.d_name, de->ud.td_name, de->td_namelen); d.d_name[de->td_namelen] = '\0'; d.d_reclen = GENERIC_DIRSIZ(&d); /* Stop reading if the directory entry we are treating is * bigger than the amount of data that can be returned. */ if (d.d_reclen > uio->uio_resid) { error = EJUSTRETURN; break; } /* Copy the new dirent structure into the output buffer and * advance pointers. */ error = uiomove(&d, d.d_reclen, uio); if (error == 0) { de = tmpfs_dir_next(node, &dc); if (cookies != NULL) { off = tmpfs_dirent_cookie(de); MPASS(*ncookies < maxcookies); cookies[(*ncookies)++] = off; } } } while (error == 0 && uio->uio_resid > 0 && de != NULL); /* Skip setting off when using cookies as it is already done above. */ if (cookies == NULL) off = tmpfs_dirent_cookie(de); /* Update the offset and cache. */ uio->uio_offset = off; node->tn_dir.tn_readdir_lastn = off; node->tn_dir.tn_readdir_lastp = de; node->tn_status |= TMPFS_NODE_ACCESSED; return error; } int tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp) { struct tmpfs_dirent *de; int error; error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL, cnp->cn_nameptr, cnp->cn_namelen, &de); if (error != 0) return (error); tmpfs_dir_attach(dvp, de); return (0); } void tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp) { struct tmpfs_dirent *de; de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp); MPASS(de != NULL && de->td_node == NULL); tmpfs_dir_detach(dvp, de); tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de); } /* * Resizes the aobj associated with the regular file pointed to by 'vp' to the * size 'newsize'. 'vp' must point to a vnode that represents a regular file. * 'newsize' must be positive. * * Returns zero on success or an appropriate error code on failure. */ int tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr) { struct tmpfs_mount *tmp; struct tmpfs_node *node; vm_object_t uobj; vm_page_t m, ma[1]; vm_pindex_t idx, newpages, oldpages; off_t oldsize; int base, rv; MPASS(vp->v_type == VREG); MPASS(newsize >= 0); node = VP_TO_TMPFS_NODE(vp); uobj = node->tn_reg.tn_aobj; tmp = VFS_TO_TMPFS(vp->v_mount); /* * Convert the old and new sizes to the number of pages needed to * store them. It may happen that we do not need to do anything * because the last allocated page can accommodate the change on * its own. */ oldsize = node->tn_size; oldpages = OFF_TO_IDX(oldsize + PAGE_MASK); MPASS(oldpages == uobj->size); newpages = OFF_TO_IDX(newsize + PAGE_MASK); if (newpages > oldpages && tmpfs_pages_check_avail(tmp, newpages - oldpages) == 0) return (ENOSPC); VM_OBJECT_WLOCK(uobj); if (newsize < oldsize) { /* * Zero the truncated part of the last page. */ base = newsize & PAGE_MASK; if (base != 0) { idx = OFF_TO_IDX(newsize); retry: m = vm_page_lookup(uobj, idx); if (m != NULL) { if (vm_page_sleep_if_busy(m, "tmfssz")) goto retry; MPASS(m->valid == VM_PAGE_BITS_ALL); } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) { m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL); if (m == NULL) { VM_OBJECT_WUNLOCK(uobj); VM_WAIT; VM_OBJECT_WLOCK(uobj); goto retry; } else if (m->valid != VM_PAGE_BITS_ALL) { ma[0] = m; rv = vm_pager_get_pages(uobj, ma, 1, 0); m = vm_page_lookup(uobj, idx); } else /* A cached page was reactivated. */ rv = VM_PAGER_OK; vm_page_lock(m); if (rv == VM_PAGER_OK) { vm_page_deactivate(m); vm_page_unlock(m); vm_page_xunbusy(m); } else { vm_page_free(m); vm_page_unlock(m); if (ignerr) m = NULL; else { VM_OBJECT_WUNLOCK(uobj); return (EIO); } } } if (m != NULL) { pmap_zero_page_area(m, base, PAGE_SIZE - base); vm_page_dirty(m); vm_pager_page_unswapped(m); } } /* * Release any swap space and free any whole pages. */ if (newpages < oldpages) { swap_pager_freespace(uobj, newpages, oldpages - newpages); vm_object_page_remove(uobj, newpages, 0, 0); } } uobj->size = newpages; VM_OBJECT_WUNLOCK(uobj); TMPFS_LOCK(tmp); tmp->tm_pages_used += (newpages - oldpages); TMPFS_UNLOCK(tmp); node->tn_size = newsize; return (0); } void tmpfs_check_mtime(struct vnode *vp) { struct tmpfs_node *node; struct vm_object *obj; ASSERT_VOP_ELOCKED(vp, "check_mtime"); if (vp->v_type != VREG) return; - node = VP_TO_TMPFS_NODE(vp); obj = vp->v_object; KASSERT((obj->flags & (OBJ_TMPFS_NODE | OBJ_TMPFS)) == (OBJ_TMPFS_NODE | OBJ_TMPFS), ("non-tmpfs obj")); /* unlocked read */ if ((obj->flags & OBJ_TMPFS_DIRTY) != 0) { VM_OBJECT_WLOCK(obj); if ((obj->flags & OBJ_TMPFS_DIRTY) != 0) { obj->flags &= ~OBJ_TMPFS_DIRTY; node = VP_TO_TMPFS_NODE(vp); node->tn_status |= TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED; } VM_OBJECT_WUNLOCK(obj); } } /* * Change flags of the given vnode. * Caller should execute tmpfs_update on vp after a successful execution. * The vnode must be locked on entry and remain locked on exit. */ int tmpfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred, struct thread *p) { int error; struct tmpfs_node *node; MPASS(VOP_ISLOCKED(vp)); node = VP_TO_TMPFS_NODE(vp); if ((flags & ~(SF_APPEND | SF_ARCHIVED | SF_IMMUTABLE | SF_NOUNLINK | UF_APPEND | UF_ARCHIVE | UF_HIDDEN | UF_IMMUTABLE | UF_NODUMP | UF_NOUNLINK | UF_OFFLINE | UF_OPAQUE | UF_READONLY | UF_REPARSE | UF_SPARSE | UF_SYSTEM)) != 0) return (EOPNOTSUPP); /* Disallow this operation if the file system is mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return EROFS; /* * Callers may only modify the file flags on objects they * have VADMIN rights for. */ if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) return (error); /* * Unprivileged processes are not permitted to unset system * flags, or modify flags if any system flags are set. */ if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) { if (node->tn_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) { error = securelevel_gt(cred, 0); if (error) return (error); } } else { if (node->tn_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || ((flags ^ node->tn_flags) & SF_SETTABLE)) return (EPERM); } node->tn_flags = flags; node->tn_status |= TMPFS_NODE_CHANGED; MPASS(VOP_ISLOCKED(vp)); return 0; } /* * Change access mode on the given vnode. * Caller should execute tmpfs_update on vp after a successful execution. * The vnode must be locked on entry and remain locked on exit. */ int tmpfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct thread *p) { int error; struct tmpfs_node *node; MPASS(VOP_ISLOCKED(vp)); node = VP_TO_TMPFS_NODE(vp); /* Disallow this operation if the file system is mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return EROFS; /* Immutable or append-only files cannot be modified, either. */ if (node->tn_flags & (IMMUTABLE | APPEND)) return EPERM; /* * To modify the permissions on a file, must possess VADMIN * for that file. */ if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) return (error); /* * Privileged processes may set the sticky bit on non-directories, * as well as set the setgid bit on a file with a group that the * process is not a member of. */ if (vp->v_type != VDIR && (mode & S_ISTXT)) { if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0)) return (EFTYPE); } if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID)) { error = priv_check_cred(cred, PRIV_VFS_SETGID, 0); if (error) return (error); } node->tn_mode &= ~ALLPERMS; node->tn_mode |= mode & ALLPERMS; node->tn_status |= TMPFS_NODE_CHANGED; MPASS(VOP_ISLOCKED(vp)); return 0; } /* * Change ownership of the given vnode. At least one of uid or gid must * be different than VNOVAL. If one is set to that value, the attribute * is unchanged. * Caller should execute tmpfs_update on vp after a successful execution. * The vnode must be locked on entry and remain locked on exit. */ int tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct thread *p) { int error; struct tmpfs_node *node; uid_t ouid; gid_t ogid; MPASS(VOP_ISLOCKED(vp)); node = VP_TO_TMPFS_NODE(vp); /* Assign default values if they are unknown. */ MPASS(uid != VNOVAL || gid != VNOVAL); if (uid == VNOVAL) uid = node->tn_uid; if (gid == VNOVAL) gid = node->tn_gid; MPASS(uid != VNOVAL && gid != VNOVAL); /* Disallow this operation if the file system is mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return EROFS; /* Immutable or append-only files cannot be modified, either. */ if (node->tn_flags & (IMMUTABLE | APPEND)) return EPERM; /* * To modify the ownership of a file, must possess VADMIN for that * file. */ if ((error = VOP_ACCESS(vp, VADMIN, cred, p))) return (error); /* * To change the owner of a file, or change the group of a file to a * group of which we are not a member, the caller must have * privilege. */ if ((uid != node->tn_uid || (gid != node->tn_gid && !groupmember(gid, cred))) && (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) return (error); ogid = node->tn_gid; ouid = node->tn_uid; node->tn_uid = uid; node->tn_gid = gid; node->tn_status |= TMPFS_NODE_CHANGED; if ((node->tn_mode & (S_ISUID | S_ISGID)) && (ouid != uid || ogid != gid)) { if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) node->tn_mode &= ~(S_ISUID | S_ISGID); } MPASS(VOP_ISLOCKED(vp)); return 0; } /* * Change size of the given vnode. * Caller should execute tmpfs_update on vp after a successful execution. * The vnode must be locked on entry and remain locked on exit. */ int tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred, struct thread *p) { int error; struct tmpfs_node *node; MPASS(VOP_ISLOCKED(vp)); node = VP_TO_TMPFS_NODE(vp); /* Decide whether this is a valid operation based on the file type. */ error = 0; switch (vp->v_type) { case VDIR: return EISDIR; case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return EROFS; break; case VBLK: /* FALLTHROUGH */ case VCHR: /* FALLTHROUGH */ case VFIFO: /* Allow modifications of special files even if in the file * system is mounted read-only (we are not modifying the * files themselves, but the objects they represent). */ return 0; default: /* Anything else is unsupported. */ return EOPNOTSUPP; } /* Immutable or append-only files cannot be modified, either. */ if (node->tn_flags & (IMMUTABLE | APPEND)) return EPERM; error = tmpfs_truncate(vp, size); /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents * for us, as will update tn_status; no need to do that here. */ MPASS(VOP_ISLOCKED(vp)); return error; } /* * Change access and modification times of the given vnode. * Caller should execute tmpfs_update on vp after a successful execution. * The vnode must be locked on entry and remain locked on exit. */ int tmpfs_chtimes(struct vnode *vp, struct vattr *vap, struct ucred *cred, struct thread *l) { int error; struct tmpfs_node *node; MPASS(VOP_ISLOCKED(vp)); node = VP_TO_TMPFS_NODE(vp); /* Disallow this operation if the file system is mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return EROFS; /* Immutable or append-only files cannot be modified, either. */ if (node->tn_flags & (IMMUTABLE | APPEND)) return EPERM; error = vn_utimes_perm(vp, vap, cred, l); if (error != 0) return (error); if (vap->va_atime.tv_sec != VNOVAL && vap->va_atime.tv_nsec != VNOVAL) node->tn_status |= TMPFS_NODE_ACCESSED; if (vap->va_mtime.tv_sec != VNOVAL && vap->va_mtime.tv_nsec != VNOVAL) node->tn_status |= TMPFS_NODE_MODIFIED; if (vap->va_birthtime.tv_nsec != VNOVAL && vap->va_birthtime.tv_nsec != VNOVAL) node->tn_status |= TMPFS_NODE_MODIFIED; tmpfs_itimes(vp, &vap->va_atime, &vap->va_mtime); if (vap->va_birthtime.tv_nsec != VNOVAL && vap->va_birthtime.tv_nsec != VNOVAL) node->tn_birthtime = vap->va_birthtime; MPASS(VOP_ISLOCKED(vp)); return 0; } /* Sync timestamps */ void tmpfs_itimes(struct vnode *vp, const struct timespec *acc, const struct timespec *mod) { struct tmpfs_node *node; struct timespec now; node = VP_TO_TMPFS_NODE(vp); if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED)) == 0) return; vfs_timestamp(&now); if (node->tn_status & TMPFS_NODE_ACCESSED) { if (acc == NULL) acc = &now; node->tn_atime = *acc; } if (node->tn_status & TMPFS_NODE_MODIFIED) { if (mod == NULL) mod = &now; node->tn_mtime = *mod; } if (node->tn_status & TMPFS_NODE_CHANGED) { node->tn_ctime = now; } node->tn_status &= ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); } void tmpfs_update(struct vnode *vp) { tmpfs_itimes(vp, NULL, NULL); } int tmpfs_truncate(struct vnode *vp, off_t length) { int error; struct tmpfs_node *node; node = VP_TO_TMPFS_NODE(vp); if (length < 0) { error = EINVAL; goto out; } if (node->tn_size == length) { error = 0; goto out; } if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) return (EFBIG); error = tmpfs_reg_resize(vp, length, FALSE); if (error == 0) { node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; } out: tmpfs_update(vp); return error; } static __inline int tmpfs_dirtree_cmp(struct tmpfs_dirent *a, struct tmpfs_dirent *b) { if (a->td_hash > b->td_hash) return (1); else if (a->td_hash < b->td_hash) return (-1); return (0); } RB_GENERATE_STATIC(tmpfs_dir, tmpfs_dirent, uh.td_entries, tmpfs_dirtree_cmp); Index: projects/building-blocks/sys/i386/include/pvclock.h =================================================================== --- projects/building-blocks/sys/i386/include/pvclock.h (nonexistent) +++ projects/building-blocks/sys/i386/include/pvclock.h (revision 278196) @@ -0,0 +1,6 @@ +/*- + * This file is in the public domain. + */ +/* $FreeBSD$ */ + +#include Property changes on: projects/building-blocks/sys/i386/include/pvclock.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/building-blocks/sys/i386/xen/clock.c =================================================================== --- projects/building-blocks/sys/i386/xen/clock.c (revision 278195) +++ projects/building-blocks/sys/i386/xen/clock.c (revision 278196) @@ -1,571 +1,570 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz and Don Ahn. * * 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. * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 */ #include __FBSDID("$FreeBSD$"); /* #define DELAYDEBUG */ /* * Routines to handle clock hardware. */ #include "opt_ddb.h" #include "opt_clock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #if defined(SMP) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we * can use a simple formula for leap years. */ #define LEAPYEAR(y) (!((y) % 4)) #define DAYSPERYEAR (28+30*4+31*7) #ifndef TIMER_FREQ #define TIMER_FREQ 1193182 #endif #ifdef CYC2NS_SCALE_FACTOR #undef CYC2NS_SCALE_FACTOR #endif #define CYC2NS_SCALE_FACTOR 10 /* Values for timerX_state: */ #define RELEASED 0 #define RELEASE_PENDING 1 #define ACQUIRED 2 #define ACQUIRE_PENDING 3 struct mtx clock_lock; #define RTC_LOCK_INIT \ mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE) #define RTC_LOCK mtx_lock_spin(&clock_lock) #define RTC_UNLOCK mtx_unlock_spin(&clock_lock) #define NS_PER_TICK (1000000000ULL/hz) int adjkerntz; /* local offset from UTC in seconds */ int clkintr_pending; int pscnt = 1; int psdiv = 1; int wall_cmos_clock; u_int timer_freq = TIMER_FREQ; static u_long cyc2ns_scale; static uint64_t processed_system_time; /* stime (ns) at last processing. */ -extern volatile uint64_t xen_timer_last_time; - #define do_div(n,base) ({ \ unsigned long __upper, __low, __high, __mod, __base; \ __base = (base); \ __asm("":"=a" (__low), "=d" (__high):"A" (n)); \ __upper = __high; \ if (__high) { \ __upper = __high % (__base); \ __high = __high / (__base); \ } \ __asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \ __asm("":"=A" (n):"a" (__low),"d" (__high)); \ __mod; \ }) /* convert from cycles(64bits) => nanoseconds (64bits) * basic equation: * ns = cycles / (freq / ns_per_sec) * ns = cycles * (ns_per_sec / freq) * ns = cycles * (10^9 / (cpu_mhz * 10^6)) * ns = cycles * (10^3 / cpu_mhz) * * Then we use scaling math (suggested by george@mvista.com) to get: * ns = cycles * (10^3 * SC / cpu_mhz) / SC * ns = cycles * cyc2ns_scale / SC * * And since SC is a constant power of two, we can convert the div * into a shift. * -johnstul@us.ibm.com "math is hard, lets go shopping!" */ static inline void set_cyc2ns_scale(unsigned long cpu_mhz) { cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz; } static inline unsigned long long cycles_2_ns(unsigned long long cyc) { return ((cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR); } static uint32_t getit(void) { - return (xen_timer_last_time); + return (pvclock_get_last_cycles()); } /* * XXX: timer needs more SMP work. */ void i8254_init(void) { RTC_LOCK_INIT; } /* * Wait "n" microseconds. * Relies on timer 1 counting down from (timer_freq / hz) * Note: timer had better have been programmed before this is first used! */ void i8254_delay(int n) { int delta, ticks_left; uint32_t tick, prev_tick; #ifdef DELAYDEBUG int getit_calls = 1; int n1; static int state = 0; if (state == 0) { state = 1; for (n1 = 1; n1 <= 10000000; n1 *= 10) DELAY(n1); state = 2; } if (state == 1) printf("DELAY(%d)...", n); #endif /* * Read the counter first, so that the rest of the setup overhead is * counted. Guess the initial overhead is 20 usec (on most systems it * takes about 1.5 usec for each of the i/o's in getit(). The loop * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The * multiplications and divisions to scale the count take a while). * * However, if ddb is active then use a fake counter since reading * the i8254 counter involves acquiring a lock. ddb must not go * locking for many reasons, but it calls here for at least atkbd * input. */ prev_tick = getit(); n -= 0; /* XXX actually guess no initial overhead */ /* * Calculate (n * (timer_freq / 1e6)) without using floating point * and without any avoidable overflows. */ if (n <= 0) ticks_left = 0; else if (n < 256) /* * Use fixed point to avoid a slow division by 1000000. * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest. * 2^15 is the first power of 2 that gives exact results * for n between 0 and 256. */ ticks_left = ((u_int)n * 39099 + (1 << 15) - 1) >> 15; else /* * Don't bother using fixed point, although gcc-2.7.2 * generates particularly poor code for the long long * division, since even the slow way will complete long * before the delay is up (unless we're interrupted). */ ticks_left = ((u_int)n * (long long)timer_freq + 999999) / 1000000; while (ticks_left > 0) { tick = getit(); #ifdef DELAYDEBUG ++getit_calls; #endif delta = tick - prev_tick; prev_tick = tick; if (delta < 0) { /* * Guard against timer0_max_count being wrong. * This shouldn't happen in normal operation, * but it may happen if set_timer_freq() is * traced. */ /* delta += timer0_max_count; ??? */ if (delta < 0) delta = 0; } ticks_left -= delta; } #ifdef DELAYDEBUG if (state == 1) printf(" %d calls to getit() at %d usec each\n", getit_calls, (n + 5) / getit_calls); #endif } void startrtclock() { uint64_t __cpu_khz; uint32_t cpu_khz; struct vcpu_time_info *info; __cpu_khz = 1000000ULL << 32; info = &HYPERVISOR_shared_info->vcpu_info[0].time; (void)do_div(__cpu_khz, info->tsc_to_system_mul); if ( info->tsc_shift < 0 ) cpu_khz = __cpu_khz << -info->tsc_shift; else cpu_khz = __cpu_khz >> info->tsc_shift; printf("Xen reported: %u.%03u MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); /* (10^6 * 2^32) / cpu_hz = (10^3 * 2^32) / cpu_khz = (2^32 * 1 / (clocks/us)) */ set_cyc2ns_scale(cpu_khz/1000); tsc_freq = cpu_khz * 1000; } /* * RTC support routines */ static __inline int readrtc(int port) { return(bcd2bin(rtcin(port))); } #ifdef XEN_PRIVILEGED_GUEST /* * Initialize the time of day register, based on the time base which is, e.g. * from a filesystem. */ static void domu_inittodr(time_t base) { unsigned long sec; int s, y; struct timespec ts; update_wallclock(); add_uptime_to_wallclock(); RTC_LOCK; if (base) { ts.tv_sec = base; ts.tv_nsec = 0; tc_setclock(&ts); } sec += tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); y = time_second - shadow_tv.tv_sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ tc_setclock(&shadow_tv); } RTC_UNLOCK; } /* * Write system time back to RTC. */ static void domu_resettodr(void) { unsigned long tm; int s; dom0_op_t op; struct shadow_time_info *shadow; struct pcpu *pc; pc = pcpu_find(smp_processor_id()); shadow = &pc->pc_shadow_time; if (xen_disable_rtc_set) return; s = splclock(); tm = time_second; splx(s); tm -= tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); if ((xen_start_info->flags & SIF_INITDOMAIN) && !independent_wallclock) { op.cmd = DOM0_SETTIME; op.u.settime.secs = tm; op.u.settime.nsecs = 0; op.u.settime.system_time = shadow->system_timestamp; HYPERVISOR_dom0_op(&op); update_wallclock(); add_uptime_to_wallclock(); } else if (independent_wallclock) { /* notyet */ ; } } /* * Initialize the time of day register, based on the time base which is, e.g. * from a filesystem. */ void inittodr(time_t base) { unsigned long sec, days; int year, month; int y, m, s; struct timespec ts; if (!(xen_start_info->flags & SIF_INITDOMAIN)) { domu_inittodr(base); return; } if (base) { s = splclock(); ts.tv_sec = base; ts.tv_nsec = 0; tc_setclock(&ts); splx(s); } /* Look if we have a RTC present and the time is valid */ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) goto wrong_time; /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ s = splhigh(); while (rtcin(RTC_STATUSA) & RTCSA_TUP) { splx(s); s = splhigh(); } days = 0; #ifdef USE_RTC_CENTURY year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100; #else year = readrtc(RTC_YEAR) + 1900; if (year < 1970) year += 100; #endif if (year < 1970) { splx(s); goto wrong_time; } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; if ((month > 2) && LEAPYEAR(year)) days ++; days += readrtc(RTC_DAY) - 1; for (y = 1970; y < year; y++) days += DAYSPERYEAR + LEAPYEAR(y); sec = ((( days * 24 + readrtc(RTC_HRS)) * 60 + readrtc(RTC_MIN)) * 60 + readrtc(RTC_SEC)); /* sec now contains the number of seconds, since Jan 1 1970, in the local time zone */ sec += tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ ts.tv_sec = sec; ts.tv_nsec = 0; tc_setclock(&ts); } splx(s); return; wrong_time: printf("Invalid time in real time clock.\n"); printf("Check and reset the date immediately!\n"); } /* * Write system time back to RTC */ void resettodr() { unsigned long tm; int y, m, s; if (!(xen_start_info->flags & SIF_INITDOMAIN)) { domu_resettodr(); return; } if (xen_disable_rtc_set) return; s = splclock(); tm = time_second; splx(s); /* Disable RTC updates and interrupts. */ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); /* Calculate local time to put in RTC */ tm -= tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */ writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */ writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */ /* We have now the days since 01-01-1970 in tm */ writertc(RTC_WDAY, (tm + 4) % 7 + 1); /* Write back Weekday */ for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y); tm >= m; y++, m = DAYSPERYEAR + LEAPYEAR(y)) tm -= m; /* Now we have the years in y and the day-of-the-year in tm */ writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */ #ifdef USE_RTC_CENTURY writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */ #endif for (m = 0; ; m++) { int ml; ml = daysinmonth[m]; if (m == 1 && LEAPYEAR(y)) ml++; if (tm < ml) break; tm -= ml; } writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */ writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */ /* Reenable RTC updates and interrupts. */ writertc(RTC_STATUSB, RTCSB_24HR); rtcin(RTC_INTR); } #endif /* * Start clocks running. */ void cpu_initclocks(void) { cpu_initclocks_bsp(); } /* Return system time offset by ticks */ uint64_t get_system_time(int ticks) { return (processed_system_time + (ticks * NS_PER_TICK)); } int timer_spkr_acquire(void) { return (0); } int timer_spkr_release(void) { return (0); } void timer_spkr_setfreq(int freq) { } Index: projects/building-blocks/sys/kern/sys_pipe.c =================================================================== --- projects/building-blocks/sys/kern/sys_pipe.c (revision 278195) +++ projects/building-blocks/sys/kern/sys_pipe.c (revision 278196) @@ -1,1843 +1,1844 @@ /*- * Copyright (c) 1996 John S. Dyson * Copyright (c) 2012 Giovanni Trematerra * 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 immediately at the beginning of the file, without modification, * 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. Absolutely no warranty of function or purpose is made by the author * John S. Dyson. * 4. Modifications may be freely made to this file if the above conditions * are met. */ /* * This file contains a high-performance replacement for the socket-based * pipes scheme originally used in FreeBSD/4.4Lite. It does not support * all features of sockets, but does do everything that pipes normally * do. */ /* * This code has two modes of operation, a small write mode and a large * write mode. The small write mode acts like conventional pipes with * a kernel buffer. If the buffer is less than PIPE_MINDIRECT, then the * "normal" pipe buffering is done. If the buffer is between PIPE_MINDIRECT * and PIPE_SIZE in size, the sending process pins the underlying pages in * memory, and the receiving process copies directly from these pinned pages * in the sending process. * * If the sending process receives a signal, it is possible that it will * go away, and certainly its address space can change, because control * is returned back to the user-mode side. In that case, the pipe code * arranges to copy the buffer supplied by the user process, to a pageable * kernel buffer, and the receiving process will grab the data from the * pageable kernel buffer. Since signals don't happen all that often, * the copy operation is normally eliminated. * * The constant PIPE_MINDIRECT is chosen to make sure that buffering will * happen for small transfers so that the system will not spend all of * its time context switching. * * In order to limit the resource use of pipes, two sysctls exist: * * kern.ipc.maxpipekva - This is a hard limit on the amount of pageable * address space available to us in pipe_map. This value is normally * autotuned, but may also be loader tuned. * * kern.ipc.pipekva - This read-only sysctl tracks the current amount of * memory in use by pipes. * * Based on how large pipekva is relative to maxpipekva, the following * will happen: * * 0% - 50%: * New pipes are given 16K of memory backing, pipes may dynamically * grow to as large as 64K where needed. * 50% - 75%: * New pipes are given 4K (or PAGE_SIZE) of memory backing, * existing pipes may NOT grow. * 75% - 100%: * New pipes are given 4K (or PAGE_SIZE) of memory backing, * existing pipes will be shrunk down to 4K whenever possible. * * Resizing may be disabled by setting kern.ipc.piperesizeallowed=0. If * that is set, the only resize that will occur is the 0 -> SMALL_PIPE_SIZE * resize which MUST occur for reverse-direction pipes when they are * first used. * * Additional information about the current state of pipes may be obtained * from kern.ipc.pipes, kern.ipc.pipefragretry, kern.ipc.pipeallocfail, * and kern.ipc.piperesizefail. * * Locking rules: There are two locks present here: A mutex, used via * PIPE_LOCK, and a flag, used via pipelock(). All locking is done via * the flag, as mutexes can not persist over uiomove. The mutex * exists only to guard access to the flag, and is not in itself a * locking mechanism. Also note that there is only a single mutex for * both directions of a pipe. * * As pipelock() may have to sleep before it can acquire the flag, it * is important to reread all data after a call to pipelock(); everything * in the structure may have changed. */ #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 /* * Use this define if you want to disable *fancy* VM things. Expect an * approx 30% decrease in transfer rate. This could be useful for * NetBSD or OpenBSD. */ /* #define PIPE_NODIRECT */ #define PIPE_PEER(pipe) \ (((pipe)->pipe_state & PIPE_NAMED) ? (pipe) : ((pipe)->pipe_peer)) /* * interfaces to the outside world */ static fo_rdwr_t pipe_read; static fo_rdwr_t pipe_write; static fo_truncate_t pipe_truncate; static fo_ioctl_t pipe_ioctl; static fo_poll_t pipe_poll; static fo_kqfilter_t pipe_kqfilter; static fo_stat_t pipe_stat; static fo_close_t pipe_close; static fo_chmod_t pipe_chmod; static fo_chown_t pipe_chown; static fo_fill_kinfo_t pipe_fill_kinfo; struct fileops pipeops = { .fo_read = pipe_read, .fo_write = pipe_write, .fo_truncate = pipe_truncate, .fo_ioctl = pipe_ioctl, .fo_poll = pipe_poll, .fo_kqfilter = pipe_kqfilter, .fo_stat = pipe_stat, .fo_close = pipe_close, .fo_chmod = pipe_chmod, .fo_chown = pipe_chown, .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = pipe_fill_kinfo, .fo_flags = DFLAG_PASSABLE }; static void filt_pipedetach(struct knote *kn); static void filt_pipedetach_notsup(struct knote *kn); static int filt_pipenotsup(struct knote *kn, long hint); static int filt_piperead(struct knote *kn, long hint); static int filt_pipewrite(struct knote *kn, long hint); static struct filterops pipe_nfiltops = { .f_isfd = 1, .f_detach = filt_pipedetach_notsup, .f_event = filt_pipenotsup }; static struct filterops pipe_rfiltops = { .f_isfd = 1, .f_detach = filt_pipedetach, .f_event = filt_piperead }; static struct filterops pipe_wfiltops = { .f_isfd = 1, .f_detach = filt_pipedetach, .f_event = filt_pipewrite }; /* * Default pipe buffer size(s), this can be kind-of large now because pipe * space is pageable. The pipe code will try to maintain locality of * reference for performance reasons, so small amounts of outstanding I/O * will not wipe the cache. */ #define MINPIPESIZE (PIPE_SIZE/3) #define MAXPIPESIZE (2*PIPE_SIZE/3) static long amountpipekva; static int pipefragretry; static int pipeallocfail; static int piperesizefail; static int piperesizeallowed = 1; SYSCTL_LONG(_kern_ipc, OID_AUTO, maxpipekva, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &maxpipekva, 0, "Pipe KVA limit"); SYSCTL_LONG(_kern_ipc, OID_AUTO, pipekva, CTLFLAG_RD, &amountpipekva, 0, "Pipe KVA usage"); SYSCTL_INT(_kern_ipc, OID_AUTO, pipefragretry, CTLFLAG_RD, &pipefragretry, 0, "Pipe allocation retries due to fragmentation"); SYSCTL_INT(_kern_ipc, OID_AUTO, pipeallocfail, CTLFLAG_RD, &pipeallocfail, 0, "Pipe allocation failures"); SYSCTL_INT(_kern_ipc, OID_AUTO, piperesizefail, CTLFLAG_RD, &piperesizefail, 0, "Pipe resize failures"); SYSCTL_INT(_kern_ipc, OID_AUTO, piperesizeallowed, CTLFLAG_RW, &piperesizeallowed, 0, "Pipe resizing allowed"); static void pipeinit(void *dummy __unused); static void pipeclose(struct pipe *cpipe); static void pipe_free_kmem(struct pipe *cpipe); static void pipe_create(struct pipe *pipe, int backing); static void pipe_paircreate(struct thread *td, struct pipepair **p_pp); static __inline int pipelock(struct pipe *cpipe, int catch); static __inline void pipeunlock(struct pipe *cpipe); #ifndef PIPE_NODIRECT static int pipe_build_write_buffer(struct pipe *wpipe, struct uio *uio); static void pipe_destroy_write_buffer(struct pipe *wpipe); static int pipe_direct_write(struct pipe *wpipe, struct uio *uio); static void pipe_clone_write_buffer(struct pipe *wpipe); #endif static int pipespace(struct pipe *cpipe, int size); static int pipespace_new(struct pipe *cpipe, int size); static int pipe_zone_ctor(void *mem, int size, void *arg, int flags); static int pipe_zone_init(void *mem, int size, int flags); static void pipe_zone_fini(void *mem, int size); static uma_zone_t pipe_zone; static struct unrhdr *pipeino_unr; static dev_t pipedev_ino; SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_ANY, pipeinit, NULL); static void pipeinit(void *dummy __unused) { pipe_zone = uma_zcreate("pipe", sizeof(struct pipepair), pipe_zone_ctor, NULL, pipe_zone_init, pipe_zone_fini, UMA_ALIGN_PTR, 0); KASSERT(pipe_zone != NULL, ("pipe_zone not initialized")); pipeino_unr = new_unrhdr(1, INT32_MAX, NULL); KASSERT(pipeino_unr != NULL, ("pipe fake inodes not initialized")); pipedev_ino = devfs_alloc_cdp_inode(); KASSERT(pipedev_ino > 0, ("pipe dev inode not initialized")); } static int pipe_zone_ctor(void *mem, int size, void *arg, int flags) { struct pipepair *pp; struct pipe *rpipe, *wpipe; KASSERT(size == sizeof(*pp), ("pipe_zone_ctor: wrong size")); pp = (struct pipepair *)mem; /* * We zero both pipe endpoints to make sure all the kmem pointers * are NULL, flag fields are zero'd, etc. We timestamp both * endpoints with the same time. */ rpipe = &pp->pp_rpipe; bzero(rpipe, sizeof(*rpipe)); vfs_timestamp(&rpipe->pipe_ctime); rpipe->pipe_atime = rpipe->pipe_mtime = rpipe->pipe_ctime; wpipe = &pp->pp_wpipe; bzero(wpipe, sizeof(*wpipe)); wpipe->pipe_ctime = rpipe->pipe_ctime; wpipe->pipe_atime = wpipe->pipe_mtime = rpipe->pipe_ctime; rpipe->pipe_peer = wpipe; rpipe->pipe_pair = pp; wpipe->pipe_peer = rpipe; wpipe->pipe_pair = pp; /* * Mark both endpoints as present; they will later get free'd * one at a time. When both are free'd, then the whole pair * is released. */ rpipe->pipe_present = PIPE_ACTIVE; wpipe->pipe_present = PIPE_ACTIVE; /* * Eventually, the MAC Framework may initialize the label * in ctor or init, but for now we do it elswhere to avoid * blocking in ctor or init. */ pp->pp_label = NULL; return (0); } static int pipe_zone_init(void *mem, int size, int flags) { struct pipepair *pp; KASSERT(size == sizeof(*pp), ("pipe_zone_init: wrong size")); pp = (struct pipepair *)mem; mtx_init(&pp->pp_mtx, "pipe mutex", NULL, MTX_DEF | MTX_NEW); return (0); } static void pipe_zone_fini(void *mem, int size) { struct pipepair *pp; KASSERT(size == sizeof(*pp), ("pipe_zone_fini: wrong size")); pp = (struct pipepair *)mem; mtx_destroy(&pp->pp_mtx); } static void pipe_paircreate(struct thread *td, struct pipepair **p_pp) { struct pipepair *pp; struct pipe *rpipe, *wpipe; *p_pp = pp = uma_zalloc(pipe_zone, M_WAITOK); #ifdef MAC /* * The MAC label is shared between the connected endpoints. As a * result mac_pipe_init() and mac_pipe_create() are called once * for the pair, and not on the endpoints. */ mac_pipe_init(pp); mac_pipe_create(td->td_ucred, pp); #endif rpipe = &pp->pp_rpipe; wpipe = &pp->pp_wpipe; knlist_init_mtx(&rpipe->pipe_sel.si_note, PIPE_MTX(rpipe)); knlist_init_mtx(&wpipe->pipe_sel.si_note, PIPE_MTX(wpipe)); /* Only the forward direction pipe is backed by default */ pipe_create(rpipe, 1); pipe_create(wpipe, 0); rpipe->pipe_state |= PIPE_DIRECTOK; wpipe->pipe_state |= PIPE_DIRECTOK; } void pipe_named_ctor(struct pipe **ppipe, struct thread *td) { struct pipepair *pp; pipe_paircreate(td, &pp); pp->pp_rpipe.pipe_state |= PIPE_NAMED; *ppipe = &pp->pp_rpipe; } void pipe_dtor(struct pipe *dpipe) { + struct pipe *peer; ino_t ino; ino = dpipe->pipe_ino; + peer = (dpipe->pipe_state & PIPE_NAMED) != 0 ? dpipe->pipe_peer : NULL; funsetown(&dpipe->pipe_sigio); pipeclose(dpipe); - if (dpipe->pipe_state & PIPE_NAMED) { - dpipe = dpipe->pipe_peer; - funsetown(&dpipe->pipe_sigio); - pipeclose(dpipe); + if (peer != NULL) { + funsetown(&peer->pipe_sigio); + pipeclose(peer); } if (ino != 0 && ino != (ino_t)-1) free_unr(pipeino_unr, ino); } /* * The pipe system call for the DTYPE_PIPE type of pipes. If we fail, let * the zone pick up the pieces via pipeclose(). */ int kern_pipe(struct thread *td, int fildes[2]) { return (kern_pipe2(td, fildes, 0)); } int kern_pipe2(struct thread *td, int fildes[2], int flags) { struct filedesc *fdp; struct file *rf, *wf; struct pipe *rpipe, *wpipe; struct pipepair *pp; int fd, fflags, error; fdp = td->td_proc->p_fd; pipe_paircreate(td, &pp); rpipe = &pp->pp_rpipe; wpipe = &pp->pp_wpipe; error = falloc(td, &rf, &fd, flags); if (error) { pipeclose(rpipe); pipeclose(wpipe); return (error); } /* An extra reference on `rf' has been held for us by falloc(). */ fildes[0] = fd; fflags = FREAD | FWRITE; if ((flags & O_NONBLOCK) != 0) fflags |= FNONBLOCK; /* * Warning: once we've gotten past allocation of the fd for the * read-side, we can only drop the read side via fdrop() in order * to avoid races against processes which manage to dup() the read * side while we are blocked trying to allocate the write side. */ finit(rf, fflags, DTYPE_PIPE, rpipe, &pipeops); error = falloc(td, &wf, &fd, flags); if (error) { fdclose(fdp, rf, fildes[0], td); fdrop(rf, td); /* rpipe has been closed by fdrop(). */ pipeclose(wpipe); return (error); } /* An extra reference on `wf' has been held for us by falloc(). */ finit(wf, fflags, DTYPE_PIPE, wpipe, &pipeops); fdrop(wf, td); fildes[1] = fd; fdrop(rf, td); return (0); } /* ARGSUSED */ int sys_pipe(struct thread *td, struct pipe_args *uap) { int error; int fildes[2]; error = kern_pipe(td, fildes); if (error) return (error); td->td_retval[0] = fildes[0]; td->td_retval[1] = fildes[1]; return (0); } int sys_pipe2(struct thread *td, struct pipe2_args *uap) { int error, fildes[2]; if (uap->flags & ~(O_CLOEXEC | O_NONBLOCK)) return (EINVAL); error = kern_pipe2(td, fildes, uap->flags); if (error) return (error); error = copyout(fildes, uap->fildes, 2 * sizeof(int)); if (error) { (void)kern_close(td, fildes[0]); (void)kern_close(td, fildes[1]); } return (error); } /* * Allocate kva for pipe circular buffer, the space is pageable * This routine will 'realloc' the size of a pipe safely, if it fails * it will retain the old buffer. * If it fails it will return ENOMEM. */ static int pipespace_new(cpipe, size) struct pipe *cpipe; int size; { caddr_t buffer; int error, cnt, firstseg; static int curfail = 0; static struct timeval lastfail; KASSERT(!mtx_owned(PIPE_MTX(cpipe)), ("pipespace: pipe mutex locked")); KASSERT(!(cpipe->pipe_state & PIPE_DIRECTW), ("pipespace: resize of direct writes not allowed")); retry: cnt = cpipe->pipe_buffer.cnt; if (cnt > size) size = cnt; size = round_page(size); buffer = (caddr_t) vm_map_min(pipe_map); error = vm_map_find(pipe_map, NULL, 0, (vm_offset_t *) &buffer, size, 0, VMFS_ANY_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error != KERN_SUCCESS) { if ((cpipe->pipe_buffer.buffer == NULL) && (size > SMALL_PIPE_SIZE)) { size = SMALL_PIPE_SIZE; pipefragretry++; goto retry; } if (cpipe->pipe_buffer.buffer == NULL) { pipeallocfail++; if (ppsratecheck(&lastfail, &curfail, 1)) printf("kern.ipc.maxpipekva exceeded; see tuning(7)\n"); } else { piperesizefail++; } return (ENOMEM); } /* copy data, then free old resources if we're resizing */ if (cnt > 0) { if (cpipe->pipe_buffer.in <= cpipe->pipe_buffer.out) { firstseg = cpipe->pipe_buffer.size - cpipe->pipe_buffer.out; bcopy(&cpipe->pipe_buffer.buffer[cpipe->pipe_buffer.out], buffer, firstseg); if ((cnt - firstseg) > 0) bcopy(cpipe->pipe_buffer.buffer, &buffer[firstseg], cpipe->pipe_buffer.in); } else { bcopy(&cpipe->pipe_buffer.buffer[cpipe->pipe_buffer.out], buffer, cnt); } } pipe_free_kmem(cpipe); cpipe->pipe_buffer.buffer = buffer; cpipe->pipe_buffer.size = size; cpipe->pipe_buffer.in = cnt; cpipe->pipe_buffer.out = 0; cpipe->pipe_buffer.cnt = cnt; atomic_add_long(&amountpipekva, cpipe->pipe_buffer.size); return (0); } /* * Wrapper for pipespace_new() that performs locking assertions. */ static int pipespace(cpipe, size) struct pipe *cpipe; int size; { KASSERT(cpipe->pipe_state & PIPE_LOCKFL, ("Unlocked pipe passed to pipespace")); return (pipespace_new(cpipe, size)); } /* * lock a pipe for I/O, blocking other access */ static __inline int pipelock(cpipe, catch) struct pipe *cpipe; int catch; { int error; PIPE_LOCK_ASSERT(cpipe, MA_OWNED); while (cpipe->pipe_state & PIPE_LOCKFL) { cpipe->pipe_state |= PIPE_LWANT; error = msleep(cpipe, PIPE_MTX(cpipe), catch ? (PRIBIO | PCATCH) : PRIBIO, "pipelk", 0); if (error != 0) return (error); } cpipe->pipe_state |= PIPE_LOCKFL; return (0); } /* * unlock a pipe I/O lock */ static __inline void pipeunlock(cpipe) struct pipe *cpipe; { PIPE_LOCK_ASSERT(cpipe, MA_OWNED); KASSERT(cpipe->pipe_state & PIPE_LOCKFL, ("Unlocked pipe passed to pipeunlock")); cpipe->pipe_state &= ~PIPE_LOCKFL; if (cpipe->pipe_state & PIPE_LWANT) { cpipe->pipe_state &= ~PIPE_LWANT; wakeup(cpipe); } } void pipeselwakeup(cpipe) struct pipe *cpipe; { PIPE_LOCK_ASSERT(cpipe, MA_OWNED); if (cpipe->pipe_state & PIPE_SEL) { selwakeuppri(&cpipe->pipe_sel, PSOCK); if (!SEL_WAITING(&cpipe->pipe_sel)) cpipe->pipe_state &= ~PIPE_SEL; } if ((cpipe->pipe_state & PIPE_ASYNC) && cpipe->pipe_sigio) pgsigio(&cpipe->pipe_sigio, SIGIO, 0); KNOTE_LOCKED(&cpipe->pipe_sel.si_note, 0); } /* * Initialize and allocate VM and memory for pipe. The structure * will start out zero'd from the ctor, so we just manage the kmem. */ static void pipe_create(pipe, backing) struct pipe *pipe; int backing; { if (backing) { /* * Note that these functions can fail if pipe map is exhausted * (as a result of too many pipes created), but we ignore the * error as it is not fatal and could be provoked by * unprivileged users. The only consequence is worse performance * with given pipe. */ if (amountpipekva > maxpipekva / 2) (void)pipespace_new(pipe, SMALL_PIPE_SIZE); else (void)pipespace_new(pipe, PIPE_SIZE); } pipe->pipe_ino = -1; } /* ARGSUSED */ static int pipe_read(fp, uio, active_cred, flags, td) struct file *fp; struct uio *uio; struct ucred *active_cred; struct thread *td; int flags; { struct pipe *rpipe; int error; int nread = 0; int size; rpipe = fp->f_data; PIPE_LOCK(rpipe); ++rpipe->pipe_busy; error = pipelock(rpipe, 1); if (error) goto unlocked_error; #ifdef MAC error = mac_pipe_check_read(active_cred, rpipe->pipe_pair); if (error) goto locked_error; #endif if (amountpipekva > (3 * maxpipekva) / 4) { if (!(rpipe->pipe_state & PIPE_DIRECTW) && (rpipe->pipe_buffer.size > SMALL_PIPE_SIZE) && (rpipe->pipe_buffer.cnt <= SMALL_PIPE_SIZE) && (piperesizeallowed == 1)) { PIPE_UNLOCK(rpipe); pipespace(rpipe, SMALL_PIPE_SIZE); PIPE_LOCK(rpipe); } } while (uio->uio_resid) { /* * normal pipe buffer receive */ if (rpipe->pipe_buffer.cnt > 0) { size = rpipe->pipe_buffer.size - rpipe->pipe_buffer.out; if (size > rpipe->pipe_buffer.cnt) size = rpipe->pipe_buffer.cnt; if (size > uio->uio_resid) size = uio->uio_resid; PIPE_UNLOCK(rpipe); error = uiomove( &rpipe->pipe_buffer.buffer[rpipe->pipe_buffer.out], size, uio); PIPE_LOCK(rpipe); if (error) break; rpipe->pipe_buffer.out += size; if (rpipe->pipe_buffer.out >= rpipe->pipe_buffer.size) rpipe->pipe_buffer.out = 0; rpipe->pipe_buffer.cnt -= size; /* * If there is no more to read in the pipe, reset * its pointers to the beginning. This improves * cache hit stats. */ if (rpipe->pipe_buffer.cnt == 0) { rpipe->pipe_buffer.in = 0; rpipe->pipe_buffer.out = 0; } nread += size; #ifndef PIPE_NODIRECT /* * Direct copy, bypassing a kernel buffer. */ } else if ((size = rpipe->pipe_map.cnt) && (rpipe->pipe_state & PIPE_DIRECTW)) { if (size > uio->uio_resid) size = (u_int) uio->uio_resid; PIPE_UNLOCK(rpipe); error = uiomove_fromphys(rpipe->pipe_map.ms, rpipe->pipe_map.pos, size, uio); PIPE_LOCK(rpipe); if (error) break; nread += size; rpipe->pipe_map.pos += size; rpipe->pipe_map.cnt -= size; if (rpipe->pipe_map.cnt == 0) { rpipe->pipe_state &= ~(PIPE_DIRECTW|PIPE_WANTW); wakeup(rpipe); } #endif } else { /* * detect EOF condition * read returns 0 on EOF, no need to set error */ if (rpipe->pipe_state & PIPE_EOF) break; /* * If the "write-side" has been blocked, wake it up now. */ if (rpipe->pipe_state & PIPE_WANTW) { rpipe->pipe_state &= ~PIPE_WANTW; wakeup(rpipe); } /* * Break if some data was read. */ if (nread > 0) break; /* * Unlock the pipe buffer for our remaining processing. * We will either break out with an error or we will * sleep and relock to loop. */ pipeunlock(rpipe); /* * Handle non-blocking mode operation or * wait for more data. */ if (fp->f_flag & FNONBLOCK) { error = EAGAIN; } else { rpipe->pipe_state |= PIPE_WANTR; if ((error = msleep(rpipe, PIPE_MTX(rpipe), PRIBIO | PCATCH, "piperd", 0)) == 0) error = pipelock(rpipe, 1); } if (error) goto unlocked_error; } } #ifdef MAC locked_error: #endif pipeunlock(rpipe); /* XXX: should probably do this before getting any locks. */ if (error == 0) vfs_timestamp(&rpipe->pipe_atime); unlocked_error: --rpipe->pipe_busy; /* * PIPE_WANT processing only makes sense if pipe_busy is 0. */ if ((rpipe->pipe_busy == 0) && (rpipe->pipe_state & PIPE_WANT)) { rpipe->pipe_state &= ~(PIPE_WANT|PIPE_WANTW); wakeup(rpipe); } else if (rpipe->pipe_buffer.cnt < MINPIPESIZE) { /* * Handle write blocking hysteresis. */ if (rpipe->pipe_state & PIPE_WANTW) { rpipe->pipe_state &= ~PIPE_WANTW; wakeup(rpipe); } } if ((rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt) >= PIPE_BUF) pipeselwakeup(rpipe); PIPE_UNLOCK(rpipe); return (error); } #ifndef PIPE_NODIRECT /* * Map the sending processes' buffer into kernel space and wire it. * This is similar to a physical write operation. */ static int pipe_build_write_buffer(wpipe, uio) struct pipe *wpipe; struct uio *uio; { u_int size; int i; PIPE_LOCK_ASSERT(wpipe, MA_NOTOWNED); KASSERT(wpipe->pipe_state & PIPE_DIRECTW, ("Clone attempt on non-direct write pipe!")); if (uio->uio_iov->iov_len > wpipe->pipe_buffer.size) size = wpipe->pipe_buffer.size; else size = uio->uio_iov->iov_len; if ((i = vm_fault_quick_hold_pages(&curproc->p_vmspace->vm_map, (vm_offset_t)uio->uio_iov->iov_base, size, VM_PROT_READ, wpipe->pipe_map.ms, PIPENPAGES)) < 0) return (EFAULT); /* * set up the control block */ wpipe->pipe_map.npages = i; wpipe->pipe_map.pos = ((vm_offset_t) uio->uio_iov->iov_base) & PAGE_MASK; wpipe->pipe_map.cnt = size; /* * and update the uio data */ uio->uio_iov->iov_len -= size; uio->uio_iov->iov_base = (char *)uio->uio_iov->iov_base + size; if (uio->uio_iov->iov_len == 0) uio->uio_iov++; uio->uio_resid -= size; uio->uio_offset += size; return (0); } /* * unmap and unwire the process buffer */ static void pipe_destroy_write_buffer(wpipe) struct pipe *wpipe; { PIPE_LOCK_ASSERT(wpipe, MA_OWNED); vm_page_unhold_pages(wpipe->pipe_map.ms, wpipe->pipe_map.npages); wpipe->pipe_map.npages = 0; } /* * In the case of a signal, the writing process might go away. This * code copies the data into the circular buffer so that the source * pages can be freed without loss of data. */ static void pipe_clone_write_buffer(wpipe) struct pipe *wpipe; { struct uio uio; struct iovec iov; int size; int pos; PIPE_LOCK_ASSERT(wpipe, MA_OWNED); size = wpipe->pipe_map.cnt; pos = wpipe->pipe_map.pos; wpipe->pipe_buffer.in = size; wpipe->pipe_buffer.out = 0; wpipe->pipe_buffer.cnt = size; wpipe->pipe_state &= ~PIPE_DIRECTW; PIPE_UNLOCK(wpipe); iov.iov_base = wpipe->pipe_buffer.buffer; iov.iov_len = size; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = size; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_READ; uio.uio_td = curthread; uiomove_fromphys(wpipe->pipe_map.ms, pos, size, &uio); PIPE_LOCK(wpipe); pipe_destroy_write_buffer(wpipe); } /* * This implements the pipe buffer write mechanism. Note that only * a direct write OR a normal pipe write can be pending at any given time. * If there are any characters in the pipe buffer, the direct write will * be deferred until the receiving process grabs all of the bytes from * the pipe buffer. Then the direct mapping write is set-up. */ static int pipe_direct_write(wpipe, uio) struct pipe *wpipe; struct uio *uio; { int error; retry: PIPE_LOCK_ASSERT(wpipe, MA_OWNED); error = pipelock(wpipe, 1); if (wpipe->pipe_state & PIPE_EOF) error = EPIPE; if (error) { pipeunlock(wpipe); goto error1; } while (wpipe->pipe_state & PIPE_DIRECTW) { if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } pipeselwakeup(wpipe); wpipe->pipe_state |= PIPE_WANTW; pipeunlock(wpipe); error = msleep(wpipe, PIPE_MTX(wpipe), PRIBIO | PCATCH, "pipdww", 0); if (error) goto error1; else goto retry; } wpipe->pipe_map.cnt = 0; /* transfer not ready yet */ if (wpipe->pipe_buffer.cnt > 0) { if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } pipeselwakeup(wpipe); wpipe->pipe_state |= PIPE_WANTW; pipeunlock(wpipe); error = msleep(wpipe, PIPE_MTX(wpipe), PRIBIO | PCATCH, "pipdwc", 0); if (error) goto error1; else goto retry; } wpipe->pipe_state |= PIPE_DIRECTW; PIPE_UNLOCK(wpipe); error = pipe_build_write_buffer(wpipe, uio); PIPE_LOCK(wpipe); if (error) { wpipe->pipe_state &= ~PIPE_DIRECTW; pipeunlock(wpipe); goto error1; } error = 0; while (!error && (wpipe->pipe_state & PIPE_DIRECTW)) { if (wpipe->pipe_state & PIPE_EOF) { pipe_destroy_write_buffer(wpipe); pipeselwakeup(wpipe); pipeunlock(wpipe); error = EPIPE; goto error1; } if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } pipeselwakeup(wpipe); wpipe->pipe_state |= PIPE_WANTW; pipeunlock(wpipe); error = msleep(wpipe, PIPE_MTX(wpipe), PRIBIO | PCATCH, "pipdwt", 0); pipelock(wpipe, 0); } if (wpipe->pipe_state & PIPE_EOF) error = EPIPE; if (wpipe->pipe_state & PIPE_DIRECTW) { /* * this bit of trickery substitutes a kernel buffer for * the process that might be going away. */ pipe_clone_write_buffer(wpipe); } else { pipe_destroy_write_buffer(wpipe); } pipeunlock(wpipe); return (error); error1: wakeup(wpipe); return (error); } #endif static int pipe_write(fp, uio, active_cred, flags, td) struct file *fp; struct uio *uio; struct ucred *active_cred; struct thread *td; int flags; { int error = 0; int desiredsize; ssize_t orig_resid; struct pipe *wpipe, *rpipe; rpipe = fp->f_data; wpipe = PIPE_PEER(rpipe); PIPE_LOCK(rpipe); error = pipelock(wpipe, 1); if (error) { PIPE_UNLOCK(rpipe); return (error); } /* * detect loss of pipe read side, issue SIGPIPE if lost. */ if (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) { pipeunlock(wpipe); PIPE_UNLOCK(rpipe); return (EPIPE); } #ifdef MAC error = mac_pipe_check_write(active_cred, wpipe->pipe_pair); if (error) { pipeunlock(wpipe); PIPE_UNLOCK(rpipe); return (error); } #endif ++wpipe->pipe_busy; /* Choose a larger size if it's advantageous */ desiredsize = max(SMALL_PIPE_SIZE, wpipe->pipe_buffer.size); while (desiredsize < wpipe->pipe_buffer.cnt + uio->uio_resid) { if (piperesizeallowed != 1) break; if (amountpipekva > maxpipekva / 2) break; if (desiredsize == BIG_PIPE_SIZE) break; desiredsize = desiredsize * 2; } /* Choose a smaller size if we're in a OOM situation */ if ((amountpipekva > (3 * maxpipekva) / 4) && (wpipe->pipe_buffer.size > SMALL_PIPE_SIZE) && (wpipe->pipe_buffer.cnt <= SMALL_PIPE_SIZE) && (piperesizeallowed == 1)) desiredsize = SMALL_PIPE_SIZE; /* Resize if the above determined that a new size was necessary */ if ((desiredsize != wpipe->pipe_buffer.size) && ((wpipe->pipe_state & PIPE_DIRECTW) == 0)) { PIPE_UNLOCK(wpipe); pipespace(wpipe, desiredsize); PIPE_LOCK(wpipe); } if (wpipe->pipe_buffer.size == 0) { /* * This can only happen for reverse direction use of pipes * in a complete OOM situation. */ error = ENOMEM; --wpipe->pipe_busy; pipeunlock(wpipe); PIPE_UNLOCK(wpipe); return (error); } pipeunlock(wpipe); orig_resid = uio->uio_resid; while (uio->uio_resid) { int space; pipelock(wpipe, 0); if (wpipe->pipe_state & PIPE_EOF) { pipeunlock(wpipe); error = EPIPE; break; } #ifndef PIPE_NODIRECT /* * If the transfer is large, we can gain performance if * we do process-to-process copies directly. * If the write is non-blocking, we don't use the * direct write mechanism. * * The direct write mechanism will detect the reader going * away on us. */ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_iov->iov_len >= PIPE_MINDIRECT && wpipe->pipe_buffer.size >= PIPE_MINDIRECT && (fp->f_flag & FNONBLOCK) == 0) { pipeunlock(wpipe); error = pipe_direct_write(wpipe, uio); if (error) break; continue; } #endif /* * Pipe buffered writes cannot be coincidental with * direct writes. We wait until the currently executing * direct write is completed before we start filling the * pipe buffer. We break out if a signal occurs or the * reader goes away. */ if (wpipe->pipe_state & PIPE_DIRECTW) { if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } pipeselwakeup(wpipe); wpipe->pipe_state |= PIPE_WANTW; pipeunlock(wpipe); error = msleep(wpipe, PIPE_MTX(rpipe), PRIBIO | PCATCH, "pipbww", 0); if (error) break; else continue; } space = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt; /* Writes of size <= PIPE_BUF must be atomic. */ if ((space < uio->uio_resid) && (orig_resid <= PIPE_BUF)) space = 0; if (space > 0) { int size; /* Transfer size */ int segsize; /* first segment to transfer */ /* * Transfer size is minimum of uio transfer * and free space in pipe buffer. */ if (space > uio->uio_resid) size = uio->uio_resid; else size = space; /* * First segment to transfer is minimum of * transfer size and contiguous space in * pipe buffer. If first segment to transfer * is less than the transfer size, we've got * a wraparound in the buffer. */ segsize = wpipe->pipe_buffer.size - wpipe->pipe_buffer.in; if (segsize > size) segsize = size; /* Transfer first segment */ PIPE_UNLOCK(rpipe); error = uiomove(&wpipe->pipe_buffer.buffer[wpipe->pipe_buffer.in], segsize, uio); PIPE_LOCK(rpipe); if (error == 0 && segsize < size) { KASSERT(wpipe->pipe_buffer.in + segsize == wpipe->pipe_buffer.size, ("Pipe buffer wraparound disappeared")); /* * Transfer remaining part now, to * support atomic writes. Wraparound * happened. */ PIPE_UNLOCK(rpipe); error = uiomove( &wpipe->pipe_buffer.buffer[0], size - segsize, uio); PIPE_LOCK(rpipe); } if (error == 0) { wpipe->pipe_buffer.in += size; if (wpipe->pipe_buffer.in >= wpipe->pipe_buffer.size) { KASSERT(wpipe->pipe_buffer.in == size - segsize + wpipe->pipe_buffer.size, ("Expected wraparound bad")); wpipe->pipe_buffer.in = size - segsize; } wpipe->pipe_buffer.cnt += size; KASSERT(wpipe->pipe_buffer.cnt <= wpipe->pipe_buffer.size, ("Pipe buffer overflow")); } pipeunlock(wpipe); if (error != 0) break; } else { /* * If the "read-side" has been blocked, wake it up now. */ if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } /* * don't block on non-blocking I/O */ if (fp->f_flag & FNONBLOCK) { error = EAGAIN; pipeunlock(wpipe); break; } /* * We have no more space and have something to offer, * wake up select/poll. */ pipeselwakeup(wpipe); wpipe->pipe_state |= PIPE_WANTW; pipeunlock(wpipe); error = msleep(wpipe, PIPE_MTX(rpipe), PRIBIO | PCATCH, "pipewr", 0); if (error != 0) break; } } pipelock(wpipe, 0); --wpipe->pipe_busy; if ((wpipe->pipe_busy == 0) && (wpipe->pipe_state & PIPE_WANT)) { wpipe->pipe_state &= ~(PIPE_WANT | PIPE_WANTR); wakeup(wpipe); } else if (wpipe->pipe_buffer.cnt > 0) { /* * If we have put any characters in the buffer, we wake up * the reader. */ if (wpipe->pipe_state & PIPE_WANTR) { wpipe->pipe_state &= ~PIPE_WANTR; wakeup(wpipe); } } /* * Don't return EPIPE if any byte was written. * EINTR and other interrupts are handled by generic I/O layer. * Do not pretend that I/O succeeded for obvious user error * like EFAULT. */ if (uio->uio_resid != orig_resid && error == EPIPE) error = 0; if (error == 0) vfs_timestamp(&wpipe->pipe_mtime); /* * We have something to offer, * wake up select/poll. */ if (wpipe->pipe_buffer.cnt) pipeselwakeup(wpipe); pipeunlock(wpipe); PIPE_UNLOCK(rpipe); return (error); } /* ARGSUSED */ static int pipe_truncate(fp, length, active_cred, td) struct file *fp; off_t length; struct ucred *active_cred; struct thread *td; { struct pipe *cpipe; int error; cpipe = fp->f_data; if (cpipe->pipe_state & PIPE_NAMED) error = vnops.fo_truncate(fp, length, active_cred, td); else error = invfo_truncate(fp, length, active_cred, td); return (error); } /* * we implement a very minimal set of ioctls for compatibility with sockets. */ static int pipe_ioctl(fp, cmd, data, active_cred, td) struct file *fp; u_long cmd; void *data; struct ucred *active_cred; struct thread *td; { struct pipe *mpipe = fp->f_data; int error; PIPE_LOCK(mpipe); #ifdef MAC error = mac_pipe_check_ioctl(active_cred, mpipe->pipe_pair, cmd, data); if (error) { PIPE_UNLOCK(mpipe); return (error); } #endif error = 0; switch (cmd) { case FIONBIO: break; case FIOASYNC: if (*(int *)data) { mpipe->pipe_state |= PIPE_ASYNC; } else { mpipe->pipe_state &= ~PIPE_ASYNC; } break; case FIONREAD: if (!(fp->f_flag & FREAD)) { *(int *)data = 0; PIPE_UNLOCK(mpipe); return (0); } if (mpipe->pipe_state & PIPE_DIRECTW) *(int *)data = mpipe->pipe_map.cnt; else *(int *)data = mpipe->pipe_buffer.cnt; break; case FIOSETOWN: PIPE_UNLOCK(mpipe); error = fsetown(*(int *)data, &mpipe->pipe_sigio); goto out_unlocked; case FIOGETOWN: *(int *)data = fgetown(&mpipe->pipe_sigio); break; /* This is deprecated, FIOSETOWN should be used instead. */ case TIOCSPGRP: PIPE_UNLOCK(mpipe); error = fsetown(-(*(int *)data), &mpipe->pipe_sigio); goto out_unlocked; /* This is deprecated, FIOGETOWN should be used instead. */ case TIOCGPGRP: *(int *)data = -fgetown(&mpipe->pipe_sigio); break; default: error = ENOTTY; break; } PIPE_UNLOCK(mpipe); out_unlocked: return (error); } static int pipe_poll(fp, events, active_cred, td) struct file *fp; int events; struct ucred *active_cred; struct thread *td; { struct pipe *rpipe; struct pipe *wpipe; int levents, revents; #ifdef MAC int error; #endif revents = 0; rpipe = fp->f_data; wpipe = PIPE_PEER(rpipe); PIPE_LOCK(rpipe); #ifdef MAC error = mac_pipe_check_poll(active_cred, rpipe->pipe_pair); if (error) goto locked_error; #endif if (fp->f_flag & FREAD && events & (POLLIN | POLLRDNORM)) if ((rpipe->pipe_state & PIPE_DIRECTW) || (rpipe->pipe_buffer.cnt > 0)) revents |= events & (POLLIN | POLLRDNORM); if (fp->f_flag & FWRITE && events & (POLLOUT | POLLWRNORM)) if (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF) || (((wpipe->pipe_state & PIPE_DIRECTW) == 0) && ((wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) >= PIPE_BUF || wpipe->pipe_buffer.size == 0))) revents |= events & (POLLOUT | POLLWRNORM); levents = events & (POLLIN | POLLINIGNEOF | POLLPRI | POLLRDNORM | POLLRDBAND); if (rpipe->pipe_state & PIPE_NAMED && fp->f_flag & FREAD && levents && fp->f_seqcount == rpipe->pipe_wgen) events |= POLLINIGNEOF; if ((events & POLLINIGNEOF) == 0) { if (rpipe->pipe_state & PIPE_EOF) { revents |= (events & (POLLIN | POLLRDNORM)); if (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) revents |= POLLHUP; } } if (revents == 0) { if (fp->f_flag & FREAD && events & (POLLIN | POLLRDNORM)) { selrecord(td, &rpipe->pipe_sel); if (SEL_WAITING(&rpipe->pipe_sel)) rpipe->pipe_state |= PIPE_SEL; } if (fp->f_flag & FWRITE && events & (POLLOUT | POLLWRNORM)) { selrecord(td, &wpipe->pipe_sel); if (SEL_WAITING(&wpipe->pipe_sel)) wpipe->pipe_state |= PIPE_SEL; } } #ifdef MAC locked_error: #endif PIPE_UNLOCK(rpipe); return (revents); } /* * We shouldn't need locks here as we're doing a read and this should * be a natural race. */ static int pipe_stat(fp, ub, active_cred, td) struct file *fp; struct stat *ub; struct ucred *active_cred; struct thread *td; { struct pipe *pipe; int new_unr; #ifdef MAC int error; #endif pipe = fp->f_data; PIPE_LOCK(pipe); #ifdef MAC error = mac_pipe_check_stat(active_cred, pipe->pipe_pair); if (error) { PIPE_UNLOCK(pipe); return (error); } #endif /* For named pipes ask the underlying filesystem. */ if (pipe->pipe_state & PIPE_NAMED) { PIPE_UNLOCK(pipe); return (vnops.fo_stat(fp, ub, active_cred, td)); } /* * Lazily allocate an inode number for the pipe. Most pipe * users do not call fstat(2) on the pipe, which means that * postponing the inode allocation until it is must be * returned to userland is useful. If alloc_unr failed, * assign st_ino zero instead of returning an error. * Special pipe_ino values: * -1 - not yet initialized; * 0 - alloc_unr failed, return 0 as st_ino forever. */ if (pipe->pipe_ino == (ino_t)-1) { new_unr = alloc_unr(pipeino_unr); if (new_unr != -1) pipe->pipe_ino = new_unr; else pipe->pipe_ino = 0; } PIPE_UNLOCK(pipe); bzero(ub, sizeof(*ub)); ub->st_mode = S_IFIFO; ub->st_blksize = PAGE_SIZE; if (pipe->pipe_state & PIPE_DIRECTW) ub->st_size = pipe->pipe_map.cnt; else ub->st_size = pipe->pipe_buffer.cnt; ub->st_blocks = (ub->st_size + ub->st_blksize - 1) / ub->st_blksize; ub->st_atim = pipe->pipe_atime; ub->st_mtim = pipe->pipe_mtime; ub->st_ctim = pipe->pipe_ctime; ub->st_uid = fp->f_cred->cr_uid; ub->st_gid = fp->f_cred->cr_gid; ub->st_dev = pipedev_ino; ub->st_ino = pipe->pipe_ino; /* * Left as 0: st_nlink, st_rdev, st_flags, st_gen. */ return (0); } /* ARGSUSED */ static int pipe_close(fp, td) struct file *fp; struct thread *td; { if (fp->f_vnode != NULL) return vnops.fo_close(fp, td); fp->f_ops = &badfileops; pipe_dtor(fp->f_data); fp->f_data = NULL; return (0); } static int pipe_chmod(struct file *fp, mode_t mode, struct ucred *active_cred, struct thread *td) { struct pipe *cpipe; int error; cpipe = fp->f_data; if (cpipe->pipe_state & PIPE_NAMED) error = vn_chmod(fp, mode, active_cred, td); else error = invfo_chmod(fp, mode, active_cred, td); return (error); } static int pipe_chown(fp, uid, gid, active_cred, td) struct file *fp; uid_t uid; gid_t gid; struct ucred *active_cred; struct thread *td; { struct pipe *cpipe; int error; cpipe = fp->f_data; if (cpipe->pipe_state & PIPE_NAMED) error = vn_chown(fp, uid, gid, active_cred, td); else error = invfo_chown(fp, uid, gid, active_cred, td); return (error); } static int pipe_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { struct pipe *pi; if (fp->f_type == DTYPE_FIFO) return (vn_fill_kinfo(fp, kif, fdp)); kif->kf_type = KF_TYPE_PIPE; pi = fp->f_data; kif->kf_un.kf_pipe.kf_pipe_addr = (uintptr_t)pi; kif->kf_un.kf_pipe.kf_pipe_peer = (uintptr_t)pi->pipe_peer; kif->kf_un.kf_pipe.kf_pipe_buffer_cnt = pi->pipe_buffer.cnt; return (0); } static void pipe_free_kmem(cpipe) struct pipe *cpipe; { KASSERT(!mtx_owned(PIPE_MTX(cpipe)), ("pipe_free_kmem: pipe mutex locked")); if (cpipe->pipe_buffer.buffer != NULL) { atomic_subtract_long(&amountpipekva, cpipe->pipe_buffer.size); vm_map_remove(pipe_map, (vm_offset_t)cpipe->pipe_buffer.buffer, (vm_offset_t)cpipe->pipe_buffer.buffer + cpipe->pipe_buffer.size); cpipe->pipe_buffer.buffer = NULL; } #ifndef PIPE_NODIRECT { cpipe->pipe_map.cnt = 0; cpipe->pipe_map.pos = 0; cpipe->pipe_map.npages = 0; } #endif } /* * shutdown the pipe */ static void pipeclose(cpipe) struct pipe *cpipe; { struct pipepair *pp; struct pipe *ppipe; KASSERT(cpipe != NULL, ("pipeclose: cpipe == NULL")); PIPE_LOCK(cpipe); pipelock(cpipe, 0); pp = cpipe->pipe_pair; pipeselwakeup(cpipe); /* * If the other side is blocked, wake it up saying that * we want to close it down. */ cpipe->pipe_state |= PIPE_EOF; while (cpipe->pipe_busy) { wakeup(cpipe); cpipe->pipe_state |= PIPE_WANT; pipeunlock(cpipe); msleep(cpipe, PIPE_MTX(cpipe), PRIBIO, "pipecl", 0); pipelock(cpipe, 0); } /* * Disconnect from peer, if any. */ ppipe = cpipe->pipe_peer; if (ppipe->pipe_present == PIPE_ACTIVE) { pipeselwakeup(ppipe); ppipe->pipe_state |= PIPE_EOF; wakeup(ppipe); KNOTE_LOCKED(&ppipe->pipe_sel.si_note, 0); } /* * Mark this endpoint as free. Release kmem resources. We * don't mark this endpoint as unused until we've finished * doing that, or the pipe might disappear out from under * us. */ PIPE_UNLOCK(cpipe); pipe_free_kmem(cpipe); PIPE_LOCK(cpipe); cpipe->pipe_present = PIPE_CLOSING; pipeunlock(cpipe); /* * knlist_clear() may sleep dropping the PIPE_MTX. Set the * PIPE_FINALIZED, that allows other end to free the * pipe_pair, only after the knotes are completely dismantled. */ knlist_clear(&cpipe->pipe_sel.si_note, 1); cpipe->pipe_present = PIPE_FINALIZED; seldrain(&cpipe->pipe_sel); knlist_destroy(&cpipe->pipe_sel.si_note); /* * If both endpoints are now closed, release the memory for the * pipe pair. If not, unlock. */ if (ppipe->pipe_present == PIPE_FINALIZED) { PIPE_UNLOCK(cpipe); #ifdef MAC mac_pipe_destroy(pp); #endif uma_zfree(pipe_zone, cpipe->pipe_pair); } else PIPE_UNLOCK(cpipe); } /*ARGSUSED*/ static int pipe_kqfilter(struct file *fp, struct knote *kn) { struct pipe *cpipe; /* * If a filter is requested that is not supported by this file * descriptor, don't return an error, but also don't ever generate an * event. */ if ((kn->kn_filter == EVFILT_READ) && !(fp->f_flag & FREAD)) { kn->kn_fop = &pipe_nfiltops; return (0); } if ((kn->kn_filter == EVFILT_WRITE) && !(fp->f_flag & FWRITE)) { kn->kn_fop = &pipe_nfiltops; return (0); } cpipe = fp->f_data; PIPE_LOCK(cpipe); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: kn->kn_fop = &pipe_wfiltops; if (cpipe->pipe_peer->pipe_present != PIPE_ACTIVE) { /* other end of pipe has been closed */ PIPE_UNLOCK(cpipe); return (EPIPE); } cpipe = PIPE_PEER(cpipe); break; default: PIPE_UNLOCK(cpipe); return (EINVAL); } kn->kn_hook = cpipe; knlist_add(&cpipe->pipe_sel.si_note, kn, 1); PIPE_UNLOCK(cpipe); return (0); } static void filt_pipedetach(struct knote *kn) { struct pipe *cpipe = kn->kn_hook; PIPE_LOCK(cpipe); knlist_remove(&cpipe->pipe_sel.si_note, kn, 1); PIPE_UNLOCK(cpipe); } /*ARGSUSED*/ static int filt_piperead(struct knote *kn, long hint) { struct pipe *rpipe = kn->kn_hook; struct pipe *wpipe = rpipe->pipe_peer; int ret; PIPE_LOCK_ASSERT(rpipe, MA_OWNED); kn->kn_data = rpipe->pipe_buffer.cnt; if ((kn->kn_data == 0) && (rpipe->pipe_state & PIPE_DIRECTW)) kn->kn_data = rpipe->pipe_map.cnt; if ((rpipe->pipe_state & PIPE_EOF) || wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) { kn->kn_flags |= EV_EOF; return (1); } ret = kn->kn_data > 0; return ret; } /*ARGSUSED*/ static int filt_pipewrite(struct knote *kn, long hint) { struct pipe *wpipe; wpipe = kn->kn_hook; PIPE_LOCK_ASSERT(wpipe, MA_OWNED); if (wpipe->pipe_present != PIPE_ACTIVE || (wpipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; kn->kn_flags |= EV_EOF; return (1); } kn->kn_data = (wpipe->pipe_buffer.size > 0) ? (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) : PIPE_BUF; if (wpipe->pipe_state & PIPE_DIRECTW) kn->kn_data = 0; return (kn->kn_data >= PIPE_BUF); } static void filt_pipedetach_notsup(struct knote *kn) { } static int filt_pipenotsup(struct knote *kn, long hint) { return (0); } Index: projects/building-blocks/sys/mips/atheros/if_argevar.h =================================================================== --- projects/building-blocks/sys/mips/atheros/if_argevar.h (revision 278195) +++ projects/building-blocks/sys/mips/atheros/if_argevar.h (revision 278196) @@ -1,181 +1,193 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __IF_ARGEVAR_H__ #define __IF_ARGEVAR_H__ #define ARGE_NPHY 32 #define ARGE_TX_RING_COUNT 128 #define ARGE_RX_RING_COUNT 128 #define ARGE_RX_DMA_SIZE ARGE_RX_RING_COUNT * sizeof(struct arge_desc) #define ARGE_TX_DMA_SIZE ARGE_TX_RING_COUNT * sizeof(struct arge_desc) #define ARGE_MAXFRAGS 8 #define ARGE_RING_ALIGN sizeof(struct arge_desc) #define ARGE_RX_ALIGN sizeof(uint32_t) #define ARGE_MAXFRAGS 8 #define ARGE_TX_RING_ADDR(sc, i) \ ((sc)->arge_rdata.arge_tx_ring_paddr + sizeof(struct arge_desc) * (i)) #define ARGE_RX_RING_ADDR(sc, i) \ ((sc)->arge_rdata.arge_rx_ring_paddr + sizeof(struct arge_desc) * (i)) #define ARGE_INC(x,y) (x) = (((x) + 1) % y) #define ARGE_MII_TIMEOUT 1000 #define ARGE_LOCK(_sc) mtx_lock(&(_sc)->arge_mtx) #define ARGE_UNLOCK(_sc) mtx_unlock(&(_sc)->arge_mtx) #define ARGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->arge_mtx, MA_OWNED) /* * register space access macros */ #define ARGE_BARRIER_READ(sc) bus_barrier(sc->arge_res, 0, 0, \ BUS_SPACE_BARRIER_READ) #define ARGE_BARRIER_WRITE(sc) bus_barrier(sc->arge_res, 0, 0, \ BUS_SPACE_BARRIER_WRITE) #define ARGE_BARRIER_RW(sc) bus_barrier(sc->arge_res, 0, 0, \ BUS_SPACE_BARRIER_READ | \ BUS_SPACE_BARRIER_WRITE) #define ARGE_WRITE(sc, reg, val) do { \ bus_write_4(sc->arge_res, (reg), (val)); \ ARGE_BARRIER_WRITE((sc)); \ } while (0) #define ARGE_READ(sc, reg) bus_read_4(sc->arge_res, (reg)) #define ARGE_SET_BITS(sc, reg, bits) \ ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) | (bits)) #define ARGE_CLEAR_BITS(sc, reg, bits) \ ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) -#define ARGE_MDIO_WRITE(_sc, _reg, _val) \ - ARGE_WRITE((_sc), (_reg), (_val)) +/* + * The linux driver code for the MDIO bus does a read-after-write + * which seems to be required on MIPS74k platforms for correct + * behaviour. + * + * So, ARGE_WRITE() does the write + barrier, and the following + * ARGE_READ() seems to flush the thing all the way through the device + * FIFO(s) before we continue issuing MDIO bus updates. + */ +#define ARGE_MDIO_WRITE(_sc, _reg, _val) \ + do { \ + ARGE_WRITE((_sc), (_reg), (_val)); \ + ARGE_READ((_sc), (_reg)); \ + } while (0) #define ARGE_MDIO_READ(_sc, _reg) \ ARGE_READ((_sc), (_reg)) #define ARGE_MDIO_BARRIER_READ(_sc) ARGE_BARRIER_READ(_sc) #define ARGE_MDIO_BARRIER_WRITE(_sc) ARGE_BARRIER_WRITE(_sc) #define ARGE_MDIO_BARRIER_RW(_sc) ARGE_BARRIER_READ_RW(_sc) #define ARGE_DESC_EMPTY (1U << 31) #define ARGE_DESC_MORE (1 << 24) #define ARGE_DESC_SIZE_MASK ((1 << 12) - 1) #define ARGE_DMASIZE(len) ((len) & ARGE_DESC_SIZE_MASK) struct arge_desc { uint32_t packet_addr; uint32_t packet_ctrl; uint32_t next_desc; uint32_t padding; }; struct arge_txdesc { struct mbuf *tx_m; bus_dmamap_t tx_dmamap; }; struct arge_rxdesc { struct mbuf *rx_m; bus_dmamap_t rx_dmamap; struct arge_desc *desc; }; struct arge_chain_data { bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tx_tag; struct arge_txdesc arge_txdesc[ARGE_TX_RING_COUNT]; bus_dma_tag_t arge_rx_tag; struct arge_rxdesc arge_rxdesc[ARGE_RX_RING_COUNT]; bus_dma_tag_t arge_tx_ring_tag; bus_dma_tag_t arge_rx_ring_tag; bus_dmamap_t arge_tx_ring_map; bus_dmamap_t arge_rx_ring_map; bus_dmamap_t arge_rx_sparemap; int arge_tx_prod; int arge_tx_cons; int arge_tx_cnt; int arge_rx_cons; }; struct arge_ring_data { struct arge_desc *arge_rx_ring; struct arge_desc *arge_tx_ring; bus_addr_t arge_rx_ring_paddr; bus_addr_t arge_tx_ring_paddr; }; /* * Allow PLL values to be overridden. */ struct arge_pll_data { uint32_t pll_10; uint32_t pll_100; uint32_t pll_1000; }; struct arge_softc { struct ifnet *arge_ifp; /* interface info */ device_t arge_dev; struct ifmedia arge_ifmedia; /* * Media & duples settings for multiPHY MAC */ uint32_t arge_media_type; uint32_t arge_duplex_mode; uint32_t arge_phymask; uint8_t arge_eaddr[ETHER_ADDR_LEN]; struct resource *arge_res; int arge_rid; struct resource *arge_irq; void *arge_intrhand; device_t arge_miibus; device_t arge_miiproxy; ar71xx_mii_mode arge_miicfg; struct arge_pll_data arge_pllcfg; bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tag; struct mtx arge_mtx; struct callout arge_stat_callout; struct task arge_link_task; struct arge_chain_data arge_cdata; struct arge_ring_data arge_rdata; int arge_link_status; int arge_detach; uint32_t arge_intr_status; int arge_mac_unit; int arge_if_flags; uint32_t arge_debug; uint32_t arge_mdiofreq; struct { uint32_t tx_pkts_unaligned; uint32_t tx_pkts_aligned; uint32_t rx_overflow; uint32_t tx_underflow; } stats; }; #endif /* __IF_ARGEVAR_H__ */ Index: projects/building-blocks/sys/netinet/ip_output.c =================================================================== --- projects/building-blocks/sys/netinet/ip_output.c (revision 278195) +++ projects/building-blocks/sys/netinet/ip_output.c (revision 278196) @@ -1,1374 +1,1378 @@ /*- * 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 #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 VNET_DEFINE(u_short, ip_id); #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 *, struct mbuf *, struct sockaddr_in *, int); extern int in_mcast_loop; extern struct protosw inetsw[]; /* * 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 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 */ struct in_addr odst; struct m_tag *fwd_tag = NULL; uint32_t fibnum; int have_ia_ref; int needfiblookup; #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); /* * Fill in IP header. If we are not allowing fragmentation, * then the ip_id field is meaningless, but we don't set it * to zero. Doing so causes various problems when devices along * the path (routers, load balancers, firewalls, etc.) illegally * disable DF on our packet. Note that a 16-bit counter * will wrap around in less than 10 seconds at 100 Mbit/s on a * medium with MTU 1500. See Steven M. Bellovin, "A Technique * for Counting NATted Hosts", Proc. IMW'02, available at * . */ if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_v = IPVERSION; ip->ip_hl = hlen >> 2; ip->ip_id = ip_newid(); 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); again: ia = NULL; have_ia_ref = 0; /* * If there is a cached route, check that it is to the same * destination and is still up. If not, free it and try again. * The address family should also be checked in case of sharing * the cache with IPv6. */ rte = ro->ro_rt; if (rte && ((rte->rt_flags & RTF_UP) == 0 || rte->rt_ifp == NULL || !RT_LINK_IS_UP(rte->rt_ifp) || dst->sin_family != AF_INET || dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { RO_RTFREE(ro); ro->ro_lle = NULL; rte = NULL; gw = dst; } if (rte == NULL && fwd_tag == NULL) { bzero(dst, sizeof(*dst)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; } /* * 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); 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_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, dst, 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)) goto passout; /* Run through list of hooks for output packets. */ odst.s_addr = ip->ip_dst.s_addr; error = pfil_run_hooks(&V_inet_pfil_hook, &m, ifp, PFIL_OUT, inp); if (error != 0 || m == NULL) goto done; ip = mtod(m, struct ip *); needfiblookup = 0; /* 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); goto done; } else { if (have_ia_ref) ifa_free(&ia->ia_ifa); 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 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); goto done; } /* 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); if (have_ia_ref) ifa_free(&ia->ia_ifa); goto again; } passout: /* 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); 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; struct mbuf *m; for (m = m0, off = 0; m && (off+m->m_len) <= mtu; m = m->m_next) off += m->m_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 flowid is the same for the fragmented mbufs */ + M_HASHTYPE_SET(m, M_HASHTYPE_GET(m0)); + m->m_pkthdr.flowid = m0->m_pkthdr.flowid; + /* copy multicast flag, if any */ m->m_flags |= (m0->m_flags & M_MCAST); /* * 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; m->m_pkthdr.rcvif = NULL; #ifdef MAC mac_netinet_fragment(m0, m); #endif m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags; 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, struct mbuf *m, struct sockaddr_in *dst, int hlen) { register 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 1 /* XXX */ if (dst->sin_family != AF_INET) { printf("ip_mloopback: bad address family %d\n", dst->sin_family); dst->sin_family = AF_INET; } #endif if_simloop(ifp, copym, dst->sin_family, 0); } } Index: projects/building-blocks/sys/ofed/include/linux/linux_idr.c =================================================================== --- projects/building-blocks/sys/ofed/include/linux/linux_idr.c (revision 278195) +++ projects/building-blocks/sys/ofed/include/linux/linux_idr.c (revision 278196) @@ -1,449 +1,449 @@ /*- * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * IDR Implementation. * * This is quick and dirty and not as re-entrant as the linux version * however it should be fairly fast. It is basically a radix tree with * a builtin bitmap for allocation. */ static MALLOC_DEFINE(M_IDR, "idr", "Linux IDR compat"); static inline int idr_max(struct idr *idr) { return (1 << (idr->layers * IDR_BITS)) - 1; } static inline int idr_pos(int id, int layer) { return (id >> (IDR_BITS * layer)) & IDR_MASK; } void idr_init(struct idr *idr) { bzero(idr, sizeof(*idr)); mtx_init(&idr->lock, "idr", NULL, MTX_DEF); } /* Only frees cached pages. */ void idr_destroy(struct idr *idr) { struct idr_layer *il, *iln; idr_remove_all(idr); mtx_lock(&idr->lock); for (il = idr->free; il != NULL; il = iln) { iln = il->ary[0]; free(il, M_IDR); } mtx_unlock(&idr->lock); } static void idr_remove_layer(struct idr_layer *il, int layer) { int i; if (il == NULL) return; if (layer == 0) { free(il, M_IDR); return; } for (i = 0; i < IDR_SIZE; i++) if (il->ary[i]) idr_remove_layer(il->ary[i], layer - 1); } void idr_remove_all(struct idr *idr) { mtx_lock(&idr->lock); idr_remove_layer(idr->top, idr->layers - 1); idr->top = NULL; idr->layers = 0; mtx_unlock(&idr->lock); } void idr_remove(struct idr *idr, int id) { struct idr_layer *il; int layer; int idx; id &= MAX_ID_MASK; mtx_lock(&idr->lock); il = idr->top; layer = idr->layers - 1; if (il == NULL || id > idr_max(idr)) { mtx_unlock(&idr->lock); return; } /* * Walk down the tree to this item setting bitmaps along the way * as we know at least one item will be free along this path. */ while (layer && il) { idx = idr_pos(id, layer); il->bitmap |= 1 << idx; il = il->ary[idx]; layer--; } idx = id & IDR_MASK; /* * At this point we've set free space bitmaps up the whole tree. * We could make this non-fatal and unwind but linux dumps a stack * and a warning so I don't think it's necessary. */ if (il == NULL || (il->bitmap & (1 << idx)) != 0) panic("idr_remove: Item %d not allocated (%p, %p)\n", id, idr, il); il->ary[idx] = NULL; il->bitmap |= 1 << idx; mtx_unlock(&idr->lock); return; } void * idr_replace(struct idr *idr, void *ptr, int id) { struct idr_layer *il; void *res; int layer; int idx; res = ERR_PTR(-EINVAL); id &= MAX_ID_MASK; mtx_lock(&idr->lock); il = idr->top; layer = idr->layers - 1; if (il == NULL || id > idr_max(idr)) goto out; while (layer && il) { il = il->ary[idr_pos(id, layer)]; layer--; } idx = id & IDR_MASK; /* * Replace still returns an error if the item was not allocated. */ if (il != NULL && (il->bitmap & (1 << idx)) != 0) { res = il->ary[idx]; il->ary[idx] = ptr; } out: mtx_unlock(&idr->lock); return (res); } void * idr_find(struct idr *idr, int id) { struct idr_layer *il; void *res; int layer; res = NULL; id &= MAX_ID_MASK; mtx_lock(&idr->lock); il = idr->top; layer = idr->layers - 1; if (il == NULL || id > idr_max(idr)) goto out; while (layer && il) { il = il->ary[idr_pos(id, layer)]; layer--; } if (il != NULL) res = il->ary[id & IDR_MASK]; out: mtx_unlock(&idr->lock); return (res); } int idr_pre_get(struct idr *idr, gfp_t gfp_mask) { struct idr_layer *il, *iln; struct idr_layer *head; int need; mtx_lock(&idr->lock); for (;;) { need = idr->layers + 1; for (il = idr->free; il != NULL; il = il->ary[0]) need--; mtx_unlock(&idr->lock); - if (need == 0) + if (need <= 0) break; for (head = NULL; need; need--) { iln = malloc(sizeof(*il), M_IDR, M_ZERO | gfp_mask); if (iln == NULL) break; bitmap_fill(&iln->bitmap, IDR_SIZE); if (head != NULL) { il->ary[0] = iln; il = iln; } else head = il = iln; } if (head == NULL) return (0); mtx_lock(&idr->lock); il->ary[0] = idr->free; idr->free = head; } return (1); } static inline struct idr_layer * idr_get(struct idr *idr) { struct idr_layer *il; il = idr->free; if (il) { idr->free = il->ary[0]; il->ary[0] = NULL; return (il); } il = malloc(sizeof(*il), M_IDR, M_ZERO | M_NOWAIT); bitmap_fill(&il->bitmap, IDR_SIZE); return (il); } /* * Could be implemented as get_new_above(idr, ptr, 0, idp) but written * first for simplicity sake. */ int idr_get_new(struct idr *idr, void *ptr, int *idp) { struct idr_layer *stack[MAX_LEVEL]; struct idr_layer *il; int error; int layer; int idx; int id; error = -EAGAIN; mtx_lock(&idr->lock); /* * Expand the tree until there is free space. */ if (idr->top == NULL || idr->top->bitmap == 0) { if (idr->layers == MAX_LEVEL + 1) { error = -ENOSPC; goto out; } il = idr_get(idr); if (il == NULL) goto out; il->ary[0] = idr->top; if (idr->top) il->bitmap &= ~1; idr->top = il; idr->layers++; } il = idr->top; id = 0; /* * Walk the tree following free bitmaps, record our path. */ for (layer = idr->layers - 1;; layer--) { stack[layer] = il; idx = ffsl(il->bitmap); if (idx == 0) panic("idr_get_new: Invalid leaf state (%p, %p)\n", idr, il); idx--; id |= idx << (layer * IDR_BITS); if (layer == 0) break; if (il->ary[idx] == NULL) { il->ary[idx] = idr_get(idr); if (il->ary[idx] == NULL) goto out; } il = il->ary[idx]; } /* * Allocate the leaf to the consumer. */ il->bitmap &= ~(1 << idx); il->ary[idx] = ptr; *idp = id; /* * Clear bitmaps potentially up to the root. */ while (il->bitmap == 0 && ++layer < idr->layers) { il = stack[layer]; il->bitmap &= ~(1 << idr_pos(id, layer)); } error = 0; out: mtx_unlock(&idr->lock); #ifdef INVARIANTS if (error == 0 && idr_find(idr, id) != ptr) { panic("idr_get_new: Failed for idr %p, id %d, ptr %p\n", idr, id, ptr); } #endif return (error); } int idr_get_new_above(struct idr *idr, void *ptr, int starting_id, int *idp) { struct idr_layer *stack[MAX_LEVEL]; struct idr_layer *il; int error; int layer; int idx, sidx; int id; error = -EAGAIN; mtx_lock(&idr->lock); /* * Compute the layers required to support starting_id and the mask * at the top layer. */ restart: idx = starting_id; layer = 0; while (idx & ~IDR_MASK) { layer++; idx >>= IDR_BITS; } if (layer == MAX_LEVEL + 1) { error = -ENOSPC; goto out; } /* * Expand the tree until there is free space at or beyond starting_id. */ while (idr->layers <= layer || idr->top->bitmap < (1 << idr_pos(starting_id, idr->layers - 1))) { if (idr->layers == MAX_LEVEL + 1) { error = -ENOSPC; goto out; } il = idr_get(idr); if (il == NULL) goto out; il->ary[0] = idr->top; if (idr->top && idr->top->bitmap == 0) il->bitmap &= ~1; idr->top = il; idr->layers++; } il = idr->top; id = 0; /* * Walk the tree following free bitmaps, record our path. */ for (layer = idr->layers - 1;; layer--) { stack[layer] = il; sidx = idr_pos(starting_id, layer); /* Returns index numbered from 0 or size if none exists. */ idx = find_next_bit(&il->bitmap, IDR_SIZE, sidx); if (idx == IDR_SIZE && sidx == 0) panic("idr_get_new: Invalid leaf state (%p, %p)\n", idr, il); /* * We may have walked a path where there was a free bit but * it was lower than what we wanted. Restart the search with * a larger starting id. id contains the progress we made so * far. Search the leaf one above this level. This may * restart as many as MAX_LEVEL times but that is expected * to be rare. */ if (idx == IDR_SIZE) { starting_id = id + (1 << ((layer + 1) * IDR_BITS)); goto restart; } if (idx > sidx) starting_id = 0; /* Search the whole subtree. */ id |= idx << (layer * IDR_BITS); if (layer == 0) break; if (il->ary[idx] == NULL) { il->ary[idx] = idr_get(idr); if (il->ary[idx] == NULL) goto out; } il = il->ary[idx]; } /* * Allocate the leaf to the consumer. */ il->bitmap &= ~(1 << idx); il->ary[idx] = ptr; *idp = id; /* * Clear bitmaps potentially up to the root. */ while (il->bitmap == 0 && ++layer < idr->layers) { il = stack[layer]; il->bitmap &= ~(1 << idr_pos(id, layer)); } error = 0; out: mtx_unlock(&idr->lock); #ifdef INVARIANTS if (error == 0 && idr_find(idr, id) != ptr) { panic("idr_get_new_above: Failed for idr %p, id %d, ptr %p\n", idr, id, ptr); } #endif return (error); } Index: projects/building-blocks/sys/rpc/svc_vc.c =================================================================== --- projects/building-blocks/sys/rpc/svc_vc.c (revision 278195) +++ projects/building-blocks/sys/rpc/svc_vc.c (revision 278196) @@ -1,979 +1,979 @@ /* $NetBSD: svc_vc.c,v 1.7 2000/08/03 00:01:53 fvdl Exp $ */ /*- * Copyright (c) 2009, Sun Microsystems, 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: * - 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. * - Neither the name of Sun Microsystems, Inc. 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 HOLDER 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 defined(LIBC_SCCS) && !defined(lint) static char *sccsid2 = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; static char *sccsid = "@(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC"; #endif #include __FBSDID("$FreeBSD$"); /* * svc_vc.c, Server side for Connection Oriented based RPC. * * Actually implements two flavors of transporter - * a tcp rendezvouser (a listner and connection establisher) * and a record/tcp stream. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool_t svc_vc_rendezvous_recv(SVCXPRT *, struct rpc_msg *, struct sockaddr **, struct mbuf **); static enum xprt_stat svc_vc_rendezvous_stat(SVCXPRT *); static void svc_vc_rendezvous_destroy(SVCXPRT *); static bool_t svc_vc_null(void); static void svc_vc_destroy(SVCXPRT *); static enum xprt_stat svc_vc_stat(SVCXPRT *); static bool_t svc_vc_ack(SVCXPRT *, uint32_t *); static bool_t svc_vc_recv(SVCXPRT *, struct rpc_msg *, struct sockaddr **, struct mbuf **); static bool_t svc_vc_reply(SVCXPRT *, struct rpc_msg *, struct sockaddr *, struct mbuf *, uint32_t *seq); static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in); static bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq, void *in); static void svc_vc_backchannel_destroy(SVCXPRT *); static enum xprt_stat svc_vc_backchannel_stat(SVCXPRT *); static bool_t svc_vc_backchannel_recv(SVCXPRT *, struct rpc_msg *, struct sockaddr **, struct mbuf **); static bool_t svc_vc_backchannel_reply(SVCXPRT *, struct rpc_msg *, struct sockaddr *, struct mbuf *, uint32_t *); static bool_t svc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq, void *in); static SVCXPRT *svc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr); static int svc_vc_accept(struct socket *head, struct socket **sop); static int svc_vc_soupcall(struct socket *so, void *arg, int waitflag); static struct xp_ops svc_vc_rendezvous_ops = { .xp_recv = svc_vc_rendezvous_recv, .xp_stat = svc_vc_rendezvous_stat, .xp_reply = (bool_t (*)(SVCXPRT *, struct rpc_msg *, struct sockaddr *, struct mbuf *, uint32_t *))svc_vc_null, .xp_destroy = svc_vc_rendezvous_destroy, .xp_control = svc_vc_rendezvous_control }; static struct xp_ops svc_vc_ops = { .xp_recv = svc_vc_recv, .xp_stat = svc_vc_stat, .xp_ack = svc_vc_ack, .xp_reply = svc_vc_reply, .xp_destroy = svc_vc_destroy, .xp_control = svc_vc_control }; static struct xp_ops svc_vc_backchannel_ops = { .xp_recv = svc_vc_backchannel_recv, .xp_stat = svc_vc_backchannel_stat, .xp_reply = svc_vc_backchannel_reply, .xp_destroy = svc_vc_backchannel_destroy, .xp_control = svc_vc_backchannel_control }; /* * Usage: * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size); * * Creates, registers, and returns a (rpc) tcp based transporter. * Once *xprt is initialized, it is registered as a transporter * see (svc.h, xprt_register). This routine returns * a NULL if a problem occurred. * * The filedescriptor passed in is expected to refer to a bound, but * not yet connected socket. * * Since streams do buffered io similar to stdio, the caller can specify * how big the send and receive buffers are via the second and third parms; * 0 => use the system default. */ SVCXPRT * svc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize, size_t recvsize) { - SVCXPRT *xprt; + SVCXPRT *xprt = NULL; struct sockaddr* sa; int error; SOCK_LOCK(so); if (so->so_state & (SS_ISCONNECTED|SS_ISDISCONNECTED)) { SOCK_UNLOCK(so); error = so->so_proto->pr_usrreqs->pru_peeraddr(so, &sa); if (error) return (NULL); xprt = svc_vc_create_conn(pool, so, sa); free(sa, M_SONAME); return (xprt); } SOCK_UNLOCK(so); xprt = svc_xprt_alloc(); sx_init(&xprt->xp_lock, "xprt->xp_lock"); xprt->xp_pool = pool; xprt->xp_socket = so; xprt->xp_p1 = NULL; xprt->xp_p2 = NULL; xprt->xp_ops = &svc_vc_rendezvous_ops; error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa); if (error) { goto cleanup_svc_vc_create; } memcpy(&xprt->xp_ltaddr, sa, sa->sa_len); free(sa, M_SONAME); xprt_register(xprt); solisten(so, SOMAXCONN, curthread); SOCKBUF_LOCK(&so->so_rcv); xprt->xp_upcallset = 1; soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt); SOCKBUF_UNLOCK(&so->so_rcv); return (xprt); cleanup_svc_vc_create: if (xprt) { sx_destroy(&xprt->xp_lock); svc_xprt_free(xprt); } return (NULL); } /* * Create a new transport for a socket optained via soaccept(). */ SVCXPRT * svc_vc_create_conn(SVCPOOL *pool, struct socket *so, struct sockaddr *raddr) { SVCXPRT *xprt = NULL; struct cf_conn *cd = NULL; struct sockaddr* sa = NULL; struct sockopt opt; int one = 1; int error; bzero(&opt, sizeof(struct sockopt)); opt.sopt_dir = SOPT_SET; opt.sopt_level = SOL_SOCKET; opt.sopt_name = SO_KEEPALIVE; opt.sopt_val = &one; opt.sopt_valsize = sizeof(one); error = sosetopt(so, &opt); if (error) { return (NULL); } if (so->so_proto->pr_protocol == IPPROTO_TCP) { bzero(&opt, sizeof(struct sockopt)); opt.sopt_dir = SOPT_SET; opt.sopt_level = IPPROTO_TCP; opt.sopt_name = TCP_NODELAY; opt.sopt_val = &one; opt.sopt_valsize = sizeof(one); error = sosetopt(so, &opt); if (error) { return (NULL); } } cd = mem_alloc(sizeof(*cd)); cd->strm_stat = XPRT_IDLE; xprt = svc_xprt_alloc(); sx_init(&xprt->xp_lock, "xprt->xp_lock"); xprt->xp_pool = pool; xprt->xp_socket = so; xprt->xp_p1 = cd; xprt->xp_p2 = NULL; xprt->xp_ops = &svc_vc_ops; /* * See http://www.connectathon.org/talks96/nfstcp.pdf - client * has a 5 minute timer, server has a 6 minute timer. */ xprt->xp_idletimeout = 6 * 60; memcpy(&xprt->xp_rtaddr, raddr, raddr->sa_len); error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa); if (error) goto cleanup_svc_vc_create; memcpy(&xprt->xp_ltaddr, sa, sa->sa_len); free(sa, M_SONAME); xprt_register(xprt); SOCKBUF_LOCK(&so->so_rcv); xprt->xp_upcallset = 1; soupcall_set(so, SO_RCV, svc_vc_soupcall, xprt); SOCKBUF_UNLOCK(&so->so_rcv); /* * Throw the transport into the active list in case it already * has some data buffered. */ sx_xlock(&xprt->xp_lock); xprt_active(xprt); sx_xunlock(&xprt->xp_lock); return (xprt); cleanup_svc_vc_create: if (xprt) { sx_destroy(&xprt->xp_lock); svc_xprt_free(xprt); } if (cd) mem_free(cd, sizeof(*cd)); return (NULL); } /* * Create a new transport for a backchannel on a clnt_vc socket. */ SVCXPRT * svc_vc_create_backchannel(SVCPOOL *pool) { SVCXPRT *xprt = NULL; struct cf_conn *cd = NULL; cd = mem_alloc(sizeof(*cd)); cd->strm_stat = XPRT_IDLE; xprt = svc_xprt_alloc(); sx_init(&xprt->xp_lock, "xprt->xp_lock"); xprt->xp_pool = pool; xprt->xp_socket = NULL; xprt->xp_p1 = cd; xprt->xp_p2 = NULL; xprt->xp_ops = &svc_vc_backchannel_ops; return (xprt); } /* * This does all of the accept except the final call to soaccept. The * caller will call soaccept after dropping its locks (soaccept may * call malloc). */ int svc_vc_accept(struct socket *head, struct socket **sop) { int error = 0; struct socket *so; if ((head->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; goto done; } #ifdef MAC error = mac_socket_check_accept(curthread->td_ucred, head); if (error != 0) goto done; #endif ACCEPT_LOCK(); if (TAILQ_EMPTY(&head->so_comp)) { ACCEPT_UNLOCK(); error = EWOULDBLOCK; goto done; } so = TAILQ_FIRST(&head->so_comp); KASSERT(!(so->so_qstate & SQ_INCOMP), ("svc_vc_accept: so SQ_INCOMP")); KASSERT(so->so_qstate & SQ_COMP, ("svc_vc_accept: so not SQ_COMP")); /* * Before changing the flags on the socket, we have to bump the * reference count. Otherwise, if the protocol calls sofree(), * the socket will be released due to a zero refcount. * XXX might not need soref() since this is simpler than kern_accept. */ SOCK_LOCK(so); /* soref() and so_state update */ soref(so); /* file descriptor reference */ TAILQ_REMOVE(&head->so_comp, so, so_list); head->so_qlen--; so->so_state |= (head->so_state & SS_NBIO); so->so_qstate &= ~SQ_COMP; so->so_head = NULL; SOCK_UNLOCK(so); ACCEPT_UNLOCK(); *sop = so; /* connection has been removed from the listen queue */ KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0); done: return (error); } /*ARGSUSED*/ static bool_t svc_vc_rendezvous_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct socket *so = NULL; struct sockaddr *sa = NULL; int error; SVCXPRT *new_xprt; /* * The socket upcall calls xprt_active() which will eventually * cause the server to call us here. We attempt to accept a * connection from the socket and turn it into a new * transport. If the accept fails, we have drained all pending * connections so we call xprt_inactive(). */ sx_xlock(&xprt->xp_lock); error = svc_vc_accept(xprt->xp_socket, &so); if (error == EWOULDBLOCK) { /* * We must re-test for new connections after taking * the lock to protect us in the case where a new * connection arrives after our call to accept fails * with EWOULDBLOCK. */ ACCEPT_LOCK(); if (TAILQ_EMPTY(&xprt->xp_socket->so_comp)) xprt_inactive_self(xprt); ACCEPT_UNLOCK(); sx_xunlock(&xprt->xp_lock); return (FALSE); } if (error) { SOCKBUF_LOCK(&xprt->xp_socket->so_rcv); if (xprt->xp_upcallset) { xprt->xp_upcallset = 0; soupcall_clear(xprt->xp_socket, SO_RCV); } SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv); xprt_inactive_self(xprt); sx_xunlock(&xprt->xp_lock); return (FALSE); } sx_xunlock(&xprt->xp_lock); sa = 0; error = soaccept(so, &sa); if (error) { /* * XXX not sure if I need to call sofree or soclose here. */ if (sa) free(sa, M_SONAME); return (FALSE); } /* * svc_vc_create_conn will call xprt_register - we don't need * to do anything with the new connection except derefence it. */ new_xprt = svc_vc_create_conn(xprt->xp_pool, so, sa); if (!new_xprt) { soclose(so); } else { SVC_RELEASE(new_xprt); } free(sa, M_SONAME); return (FALSE); /* there is never an rpc msg to be processed */ } /*ARGSUSED*/ static enum xprt_stat svc_vc_rendezvous_stat(SVCXPRT *xprt) { return (XPRT_IDLE); } static void svc_vc_destroy_common(SVCXPRT *xprt) { SOCKBUF_LOCK(&xprt->xp_socket->so_rcv); if (xprt->xp_upcallset) { xprt->xp_upcallset = 0; soupcall_clear(xprt->xp_socket, SO_RCV); } SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv); if (xprt->xp_socket) (void)soclose(xprt->xp_socket); if (xprt->xp_netid) (void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1); svc_xprt_free(xprt); } static void svc_vc_rendezvous_destroy(SVCXPRT *xprt) { svc_vc_destroy_common(xprt); } static void svc_vc_destroy(SVCXPRT *xprt) { struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1; svc_vc_destroy_common(xprt); if (cd->mreq) m_freem(cd->mreq); if (cd->mpending) m_freem(cd->mpending); mem_free(cd, sizeof(*cd)); } static void svc_vc_backchannel_destroy(SVCXPRT *xprt) { struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1; struct mbuf *m, *m2; svc_xprt_free(xprt); m = cd->mreq; while (m != NULL) { m2 = m; m = m->m_nextpkt; m_freem(m2); } mem_free(cd, sizeof(*cd)); } /*ARGSUSED*/ static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in) { return (FALSE); } static bool_t svc_vc_rendezvous_control(SVCXPRT *xprt, const u_int rq, void *in) { return (FALSE); } static bool_t svc_vc_backchannel_control(SVCXPRT *xprt, const u_int rq, void *in) { return (FALSE); } static enum xprt_stat svc_vc_stat(SVCXPRT *xprt) { struct cf_conn *cd; cd = (struct cf_conn *)(xprt->xp_p1); if (cd->strm_stat == XPRT_DIED) return (XPRT_DIED); if (cd->mreq != NULL && cd->resid == 0 && cd->eor) return (XPRT_MOREREQS); if (soreadable(xprt->xp_socket)) return (XPRT_MOREREQS); return (XPRT_IDLE); } static bool_t svc_vc_ack(SVCXPRT *xprt, uint32_t *ack) { *ack = atomic_load_acq_32(&xprt->xp_snt_cnt); *ack -= sbused(&xprt->xp_socket->so_snd); return (TRUE); } static enum xprt_stat svc_vc_backchannel_stat(SVCXPRT *xprt) { struct cf_conn *cd; cd = (struct cf_conn *)(xprt->xp_p1); if (cd->mreq != NULL) return (XPRT_MOREREQS); return (XPRT_IDLE); } /* * If we have an mbuf chain in cd->mpending, try to parse a record from it, * leaving the result in cd->mreq. If we don't have a complete record, leave * the partial result in cd->mreq and try to read more from the socket. */ static int svc_vc_process_pending(SVCXPRT *xprt) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct socket *so = xprt->xp_socket; struct mbuf *m; /* * If cd->resid is non-zero, we have part of the * record already, otherwise we are expecting a record * marker. */ if (!cd->resid && cd->mpending) { /* * See if there is enough data buffered to * make up a record marker. Make sure we can * handle the case where the record marker is * split across more than one mbuf. */ size_t n = 0; uint32_t header; m = cd->mpending; while (n < sizeof(uint32_t) && m) { n += m->m_len; m = m->m_next; } if (n < sizeof(uint32_t)) { so->so_rcv.sb_lowat = sizeof(uint32_t) - n; return (FALSE); } m_copydata(cd->mpending, 0, sizeof(header), (char *)&header); header = ntohl(header); cd->eor = (header & 0x80000000) != 0; cd->resid = header & 0x7fffffff; m_adj(cd->mpending, sizeof(uint32_t)); } /* * Start pulling off mbufs from cd->mpending * until we either have a complete record or * we run out of data. We use m_split to pull * data - it will pull as much as possible and * split the last mbuf if necessary. */ while (cd->mpending && cd->resid) { m = cd->mpending; if (cd->mpending->m_next || cd->mpending->m_len > cd->resid) cd->mpending = m_split(cd->mpending, cd->resid, M_WAITOK); else cd->mpending = NULL; if (cd->mreq) m_last(cd->mreq)->m_next = m; else cd->mreq = m; while (m) { cd->resid -= m->m_len; m = m->m_next; } } /* * Block receive upcalls if we have more data pending, * otherwise report our need. */ if (cd->mpending) so->so_rcv.sb_lowat = INT_MAX; else so->so_rcv.sb_lowat = imax(1, imin(cd->resid, so->so_rcv.sb_hiwat / 2)); return (TRUE); } static bool_t svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct uio uio; struct mbuf *m; struct socket* so = xprt->xp_socket; XDR xdrs; int error, rcvflag; uint32_t xid_plus_direction[2]; /* * Serialise access to the socket and our own record parsing * state. */ sx_xlock(&xprt->xp_lock); for (;;) { /* If we have no request ready, check pending queue. */ while (cd->mpending && (cd->mreq == NULL || cd->resid != 0 || !cd->eor)) { if (!svc_vc_process_pending(xprt)) break; } /* Process and return complete request in cd->mreq. */ if (cd->mreq != NULL && cd->resid == 0 && cd->eor) { /* * Now, check for a backchannel reply. * The XID is in the first uint32_t of the reply * and the message direction is the second one. */ if ((cd->mreq->m_len >= sizeof(xid_plus_direction) || m_length(cd->mreq, NULL) >= sizeof(xid_plus_direction)) && xprt->xp_p2 != NULL) { m_copydata(cd->mreq, 0, sizeof(xid_plus_direction), (char *)xid_plus_direction); xid_plus_direction[0] = ntohl(xid_plus_direction[0]); xid_plus_direction[1] = ntohl(xid_plus_direction[1]); /* Check message direction. */ if (xid_plus_direction[1] == REPLY) { clnt_bck_svccall(xprt->xp_p2, cd->mreq, xid_plus_direction[0]); cd->mreq = NULL; continue; } } xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE); cd->mreq = NULL; /* Check for next request in a pending queue. */ svc_vc_process_pending(xprt); if (cd->mreq == NULL || cd->resid != 0) { SOCKBUF_LOCK(&so->so_rcv); if (!soreadable(so)) xprt_inactive_self(xprt); SOCKBUF_UNLOCK(&so->so_rcv); } sx_xunlock(&xprt->xp_lock); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = NULL; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); } /* * The socket upcall calls xprt_active() which will eventually * cause the server to call us here. We attempt to * read as much as possible from the socket and put * the result in cd->mpending. If the read fails, * we have drained both cd->mpending and the socket so * we can call xprt_inactive(). */ uio.uio_resid = 1000000000; uio.uio_td = curthread; m = NULL; rcvflag = MSG_DONTWAIT; error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag); if (error == EWOULDBLOCK) { /* * We must re-test for readability after * taking the lock to protect us in the case * where a new packet arrives on the socket * after our call to soreceive fails with * EWOULDBLOCK. */ SOCKBUF_LOCK(&so->so_rcv); if (!soreadable(so)) xprt_inactive_self(xprt); SOCKBUF_UNLOCK(&so->so_rcv); sx_xunlock(&xprt->xp_lock); return (FALSE); } if (error) { SOCKBUF_LOCK(&so->so_rcv); if (xprt->xp_upcallset) { xprt->xp_upcallset = 0; soupcall_clear(so, SO_RCV); } SOCKBUF_UNLOCK(&so->so_rcv); xprt_inactive_self(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (!m) { /* * EOF - the other end has closed the socket. */ xprt_inactive_self(xprt); cd->strm_stat = XPRT_DIED; sx_xunlock(&xprt->xp_lock); return (FALSE); } if (cd->mpending) m_last(cd->mpending)->m_next = m; else cd->mpending = m; } } static bool_t svc_vc_backchannel_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr **addrp, struct mbuf **mp) { struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1; struct ct_data *ct; struct mbuf *m; XDR xdrs; sx_xlock(&xprt->xp_lock); ct = (struct ct_data *)xprt->xp_p2; if (ct == NULL) { sx_xunlock(&xprt->xp_lock); return (FALSE); } mtx_lock(&ct->ct_lock); m = cd->mreq; if (m == NULL) { xprt_inactive_self(xprt); mtx_unlock(&ct->ct_lock); sx_xunlock(&xprt->xp_lock); return (FALSE); } cd->mreq = m->m_nextpkt; mtx_unlock(&ct->ct_lock); sx_xunlock(&xprt->xp_lock); xdrmbuf_create(&xdrs, m, XDR_DECODE); if (! xdr_callmsg(&xdrs, msg)) { XDR_DESTROY(&xdrs); return (FALSE); } *addrp = NULL; *mp = xdrmbuf_getall(&xdrs); XDR_DESTROY(&xdrs); return (TRUE); } static bool_t svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr *addr, struct mbuf *m, uint32_t *seq) { XDR xdrs; struct mbuf *mrep; bool_t stat = TRUE; int error, len; /* * Leave space for record mark. */ mrep = m_gethdr(M_WAITOK, MT_DATA); mrep->m_data += sizeof(uint32_t); xdrmbuf_create(&xdrs, mrep, XDR_ENCODE); if (msg->rm_reply.rp_stat == MSG_ACCEPTED && msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { if (!xdr_replymsg(&xdrs, msg)) stat = FALSE; else xdrmbuf_append(&xdrs, m); } else { stat = xdr_replymsg(&xdrs, msg); } if (stat) { m_fixhdr(mrep); /* * Prepend a record marker containing the reply length. */ M_PREPEND(mrep, sizeof(uint32_t), M_WAITOK); len = mrep->m_pkthdr.len; *mtod(mrep, uint32_t *) = htonl(0x80000000 | (len - sizeof(uint32_t))); atomic_add_acq_32(&xprt->xp_snd_cnt, len); error = sosend(xprt->xp_socket, NULL, NULL, mrep, NULL, 0, curthread); if (!error) { atomic_add_rel_32(&xprt->xp_snt_cnt, len); if (seq) *seq = xprt->xp_snd_cnt; stat = TRUE; } else atomic_subtract_32(&xprt->xp_snd_cnt, len); } else { m_freem(mrep); } XDR_DESTROY(&xdrs); return (stat); } static bool_t svc_vc_backchannel_reply(SVCXPRT *xprt, struct rpc_msg *msg, struct sockaddr *addr, struct mbuf *m, uint32_t *seq) { struct ct_data *ct; XDR xdrs; struct mbuf *mrep; bool_t stat = TRUE; int error; /* * Leave space for record mark. */ mrep = m_gethdr(M_WAITOK, MT_DATA); mrep->m_data += sizeof(uint32_t); xdrmbuf_create(&xdrs, mrep, XDR_ENCODE); if (msg->rm_reply.rp_stat == MSG_ACCEPTED && msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { if (!xdr_replymsg(&xdrs, msg)) stat = FALSE; else xdrmbuf_append(&xdrs, m); } else { stat = xdr_replymsg(&xdrs, msg); } if (stat) { m_fixhdr(mrep); /* * Prepend a record marker containing the reply length. */ M_PREPEND(mrep, sizeof(uint32_t), M_WAITOK); *mtod(mrep, uint32_t *) = htonl(0x80000000 | (mrep->m_pkthdr.len - sizeof(uint32_t))); sx_xlock(&xprt->xp_lock); ct = (struct ct_data *)xprt->xp_p2; if (ct != NULL) error = sosend(ct->ct_socket, NULL, NULL, mrep, NULL, 0, curthread); else error = EPIPE; sx_xunlock(&xprt->xp_lock); if (!error) { stat = TRUE; } } else { m_freem(mrep); } XDR_DESTROY(&xdrs); return (stat); } static bool_t svc_vc_null() { return (FALSE); } static int svc_vc_soupcall(struct socket *so, void *arg, int waitflag) { SVCXPRT *xprt = (SVCXPRT *) arg; if (soreadable(xprt->xp_socket)) xprt_active(xprt); return (SU_OK); } #if 0 /* * Get the effective UID of the sending process. Used by rpcbind, keyserv * and rpc.yppasswdd on AF_LOCAL. */ int __rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) { int sock, ret; gid_t egid; uid_t euid; struct sockaddr *sa; sock = transp->xp_fd; sa = (struct sockaddr *)transp->xp_rtaddr; if (sa->sa_family == AF_LOCAL) { ret = getpeereid(sock, &euid, &egid); if (ret == 0) *uid = euid; return (ret); } else return (-1); } #endif Index: projects/building-blocks/sys/x86/include/pvclock.h =================================================================== --- projects/building-blocks/sys/x86/include/pvclock.h (nonexistent) +++ projects/building-blocks/sys/x86/include/pvclock.h (revision 278196) @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2014, Bryan Venteicher + * 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 X86_PVCLOCK +#define X86_PVCLOCK + +struct pvclock_vcpu_time_info { + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; + uint64_t system_time; + uint32_t tsc_to_system_mul; + int8_t tsc_shift; + uint8_t flags; + uint8_t pad[2]; +}; + +#define PVCLOCK_FLAG_TSC_STABLE 0x01 +#define PVCLOCK_FLAG_GUEST_PASUED 0x02 + +struct pvclock_wall_clock { + uint32_t version; + uint32_t sec; + uint32_t nsec; +}; + +void pvclock_resume(void); +uint64_t pvclock_get_last_cycles(void); +uint64_t pvclock_tsc_freq(struct pvclock_vcpu_time_info *ti); +uint64_t pvclock_get_timecount(struct pvclock_vcpu_time_info *ti); +void pvclock_get_wallclock(struct pvclock_wall_clock *wc, + struct timespec *ts); + +#endif Property changes on: projects/building-blocks/sys/x86/include/pvclock.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/building-blocks/sys/x86/x86/pvclock.c =================================================================== --- projects/building-blocks/sys/x86/x86/pvclock.c (nonexistent) +++ projects/building-blocks/sys/x86/x86/pvclock.c (revision 278196) @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 2009 Adrian Chadd + * Copyright (c) 2012 Spectra Logic Corporation + * Copyright (c) 2014 Bryan Venteicher + * 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 +#include +#include + +#include +#include +#include +#include + +/* + * Last time; this guarantees a monotonically increasing clock for when + * a stable TSC is not provided. + */ +static volatile uint64_t pvclock_last_cycles; + +void +pvclock_resume(void) +{ + + atomic_store_rel_64(&pvclock_last_cycles, 0); +} + +uint64_t +pvclock_get_last_cycles(void) +{ + + return (atomic_load_acq_64(&pvclock_last_cycles)); +} + +uint64_t +pvclock_tsc_freq(struct pvclock_vcpu_time_info *ti) +{ + uint64_t freq; + + freq = (1000000000ULL << 32) / ti->tsc_to_system_mul; + + if (ti->tsc_shift < 0) + freq <<= -ti->tsc_shift; + else + freq >>= ti->tsc_shift; + + return (freq); +} + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline uint64_t +pvclock_scale_delta(uint64_t delta, uint32_t mul_frac, int shift) +{ + uint64_t product; + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#if defined(__i386__) + { + uint32_t tmp1, tmp2; + + /** + * For i386, the formula looks like: + * + * lower = (mul_frac * (delta & UINT_MAX)) >> 32 + * upper = mul_frac * (delta >> 32) + * product = lower + upper + */ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((uint32_t)delta), "1" ((uint32_t)(delta >> 32)), + "2" (mul_frac) ); + } +#elif defined(__amd64__) + { + unsigned long tmp; + + __asm__ ( + "mulq %[mul_frac] ; shrd $32, %[hi], %[lo]" + : [lo]"=a" (product), [hi]"=d" (tmp) + : "0" (delta), [mul_frac]"rm"((uint64_t)mul_frac)); + } +#else +#error "pvclock: unsupported x86 architecture?" +#endif + + return (product); +} + +static uint64_t +pvclock_get_nsec_offset(struct pvclock_vcpu_time_info *ti) +{ + uint64_t delta; + + delta = rdtsc() - ti->tsc_timestamp; + + return (pvclock_scale_delta(delta, ti->tsc_to_system_mul, + ti->tsc_shift)); +} + +static void +pvclock_read_time_info(struct pvclock_vcpu_time_info *ti, + uint64_t *cycles, uint8_t *flags) +{ + uint32_t version; + + do { + version = ti->version; + rmb(); + *cycles = ti->system_time + pvclock_get_nsec_offset(ti); + *flags = ti->flags; + rmb(); + } while ((ti->version & 1) != 0 || ti->version != version); +} + +static void +pvclock_read_wall_clock(struct pvclock_wall_clock *wc, uint32_t *sec, + uint32_t *nsec) +{ + uint32_t version; + + do { + version = wc->version; + rmb(); + *sec = wc->sec; + *nsec = wc->nsec; + rmb(); + } while ((wc->version & 1) != 0 || wc->version != version); +} + +uint64_t +pvclock_get_timecount(struct pvclock_vcpu_time_info *ti) +{ + uint64_t now, last; + uint8_t flags; + + pvclock_read_time_info(ti, &now, &flags); + + if (flags & PVCLOCK_FLAG_TSC_STABLE) + return (now); + + /* + * Enforce a monotonically increasing clock time across all VCPUs. + * If our time is too old, use the last time and return. Otherwise, + * try to update the last time. + */ + do { + last = atomic_load_acq_64(&pvclock_last_cycles); + if (last > now) + return (last); + } while (!atomic_cmpset_64(&pvclock_last_cycles, last, now)); + + return (now); +} + +void +pvclock_get_wallclock(struct pvclock_wall_clock *wc, struct timespec *ts) +{ + uint32_t sec, nsec; + + pvclock_read_wall_clock(wc, &sec, &nsec); + ts->tv_sec = sec; + ts->tv_nsec = nsec; +} Property changes on: projects/building-blocks/sys/x86/x86/pvclock.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/building-blocks/sys =================================================================== --- projects/building-blocks/sys (revision 278195) +++ projects/building-blocks/sys (revision 278196) Property changes on: projects/building-blocks/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r278050-278194 Index: projects/building-blocks/tools/build/mk/OptionalObsoleteFiles.inc =================================================================== --- projects/building-blocks/tools/build/mk/OptionalObsoleteFiles.inc (revision 278195) +++ projects/building-blocks/tools/build/mk/OptionalObsoleteFiles.inc (revision 278196) @@ -1,7165 +1,9187 @@ # # $FreeBSD$ # # This file add support for the WITHOUT_* and WITH_* knobs in src.conf(5) to # the check-old and delete-old* targets. # .if ${MK_ACCT} == no OLD_FILES+=etc/rc.d/accounting OLD_FILES+=etc/periodic/daily/310.accounting OLD_FILES+=usr/sbin/accton OLD_FILES+=usr/sbin/sa OLD_FILES+=usr/share/man/man8/accton.8.gz OLD_FILES+=usr/share/man/man8/sa.8.gz .endif .if ${MK_ACPI} == no OLD_FILES+=etc/devd/asus.conf OLD_FILES+=etc/rc.d/power_profile OLD_FILES+=usr/sbin/acpiconf OLD_FILES+=usr/sbin/acpidb OLD_FILES+=usr/sbin/acpidump OLD_FILES+=usr/sbin/iasl OLD_FILES+=usr/share/man/man8/acpiconf.8.gz OLD_FILES+=usr/share/man/man8/acpidb.8.gz OLD_FILES+=usr/share/man/man8/acpidump.8.gz OLD_FILES+=usr/share/man/man8/iasl.8.gz .endif .if ${MK_AMD} == no OLD_FILES+=etc/amd.map OLD_FILES+=etc/rc.d/amd OLD_FILES+=usr/bin/pawd OLD_FILES+=usr/sbin/amd OLD_FILES+=usr/sbin/amq OLD_FILES+=usr/sbin/fixmount OLD_FILES+=usr/sbin/fsinfo OLD_FILES+=usr/sbin/hlfsd OLD_FILES+=usr/sbin/mk-amd-map OLD_FILES+=usr/sbin/wire-test OLD_FILES+=usr/share/examples/etc/amd.map OLD_FILES+=usr/share/info/am-utils.info.gz OLD_FILES+=usr/share/man/man1/pawd.1.gz OLD_FILES+=usr/share/man/man5/amd.conf.5.gz OLD_FILES+=usr/share/man/man8/amd.8.gz OLD_FILES+=usr/share/man/man8/amq.8.gz OLD_FILES+=usr/share/man/man8/fixmount.8.gz OLD_FILES+=usr/share/man/man8/fsinfo.8.gz OLD_FILES+=usr/share/man/man8/hlfsd.8.gz OLD_FILES+=usr/share/man/man8/mk-amd-map.8.gz OLD_FILES+=usr/share/man/man8/wire-test.8.gz .endif .if ${MK_APM} == no OLD_FILES+=etc/rc.d/apm OLD_FILES+=etc/rc.d/apmd OLD_FILES+=etc/apmd.conf OLD_FILES+=usr/sbin/apm OLD_FILES+=usr/share/examples/etc/apmd.conf OLD_FILES+=usr/share/man/man8/amd64/apm.8.gz OLD_FILES+=usr/share/man/man8/amd64/apmconf.8.gz .endif .if ${MK_APM} == no && ${MK_ACPI} == no OLD_FILES+=etc/rc.d/powerd OLD_FILES+=usr/sbin/powerd OLD_FILES+=usr/sbin/zzz OLD_FILES+=usr/share/man/man8/zzz.8.gz OLD_FILES+=usr/share/man/man8/powerd.8.gz .endif .if ${MK_AT} == no OLD_FILES+=etc/pam.d/atrun OLD_FILES+=usr/bin/at OLD_FILES+=usr/bin/atq OLD_FILES+=usr/bin/atrm OLD_FILES+=usr/bin/batch OLD_FILES+=usr/libexec/atrun OLD_FILES+=usr/share/man/man1/at.1.gz OLD_FILES+=usr/share/man/man1/atq.1.gz OLD_FILES+=usr/share/man/man1/atrm.1.gz OLD_FILES+=usr/share/man/man1/batch.1.gz OLD_FILES+=usr/share/man/man8/atrun.8.gz .endif .if ${MK_ATM} == no OLD_FILES+=etc/rc.d/atm1 OLD_FILES+=etc/rc.d/atm2 OLD_FILES+=etc/rc.d/atm3 OLD_FILES+=rescue/atmconfig OLD_FILES+=sbin/atmconfig OLD_FILES+=usr/bin/sscop OLD_FILES+=usr/include/bsnmp/snmp_atm.h OLD_FILES+=usr/include/netnatm/addr.h OLD_FILES+=usr/include/netnatm/api/atmapi.h OLD_FILES+=usr/include/netnatm/api/ccatm.h OLD_FILES+=usr/include/netnatm/api/unisap.h OLD_DIRS+=usr/include/netnatm/api OLD_FILES+=usr/include/netnatm/msg/uni_config.h OLD_FILES+=usr/include/netnatm/msg/uni_hdr.h OLD_FILES+=usr/include/netnatm/msg/uni_ie.h OLD_FILES+=usr/include/netnatm/msg/uni_msg.h OLD_FILES+=usr/include/netnatm/msg/unimsglib.h OLD_FILES+=usr/include/netnatm/msg/uniprint.h OLD_FILES+=usr/include/netnatm/msg/unistruct.h OLD_DIRS+=usr/include/netnatm/msg OLD_FILES+=usr/include/netnatm/saal/sscfu.h OLD_FILES+=usr/include/netnatm/saal/sscfudef.h OLD_FILES+=usr/include/netnatm/saal/sscop.h OLD_FILES+=usr/include/netnatm/saal/sscopdef.h OLD_DIRS+=usr/include/netnatm/saal OLD_FILES+=usr/include/netnatm/sig/uni.h OLD_FILES+=usr/include/netnatm/sig/unidef.h OLD_FILES+=usr/include/netnatm/sig/unisig.h OLD_DIRS+=usr/include/netnatm/sig OLD_FILES+=usr/include/netnatm/unimsg.h OLD_FILES+=usr/lib/libngatm.a OLD_FILES+=usr/lib/libngatm.so OLD_LIBS+=usr/lib/libngatm.so.4 OLD_FILES+=usr/lib/libngatm_p.a OLD_FILES+=usr/lib/snmp_atm.so OLD_LIBS+=usr/lib/snmp_atm.so.6 .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libngatm.a OLD_FILES+=usr/lib32/libngatm.so OLD_LIBS+=usr/lib32/libngatm.so.4 OLD_FILES+=usr/lib32/libngatm_p.a .endif OLD_FILES+=usr/share/doc/atm/atmconfig.help OLD_FILES+=usr/share/doc/atm/atmconfig_device.help OLD_DIRS+=usr/share/doc/atm OLD_FILES+=usr/share/man/man1/sscop.1.gz OLD_FILES+=usr/share/man/man3/libngatm.3.gz OLD_FILES+=usr/share/man/man3/snmp_atm.3.gz OLD_FILES+=usr/share/man/man3/uniaddr.3.gz OLD_FILES+=usr/share/man/man3/unifunc.3.gz OLD_FILES+=usr/share/man/man3/unimsg.3.gz OLD_FILES+=usr/share/man/man3/unisap.3.gz OLD_FILES+=usr/share/man/man3/unistruct.3.gz OLD_FILES+=usr/share/man/man8/atmconfig.8.gz OLD_FILES+=usr/share/snmp/defs/atm_freebsd.def OLD_FILES+=usr/share/snmp/defs/atm_tree.def OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-ATM-FREEBSD-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-ATM.txt .endif .if ${MK_AUDIT} == no OLD_FILES+=etc/rc.d/auditd OLD_FILES+=etc/rc.d/auditdistd OLD_FILES+=etc/security/audit_class OLD_FILES+=etc/security/audit_control OLD_FILES+=etc/security/audit_event OLD_FILES+=etc/security/audit_user OLD_FILES+=etc/security/audit_warn # XXX: needs refactoring in sys/sys before it can be removed #OLD_FILES+=usr/include/bsm/audit_uevents.h #OLD_FILES+=usr/include/bsm/libbsm.h OLD_FILES+=usr/lib/libauditd.a OLD_LIBS+=usr/lib/libauditd.so.5 OLD_FILES+=usr/lib/libauditd_p.a OLD_FILES+=usr/lib/libbsm.a OLD_LIBS+=usr/lib/libbsm.so.3 OLD_FILES+=usr/lib/libbsm_p.a OLD_FILES+=usr/sbin/audit OLD_FILES+=usr/sbin/auditd OLD_FILES+=usr/sbin/auditreduce OLD_FILES+=usr/sbin/praudit OLD_FILES+=usr/share/man/man1/auditreduce.1.gz OLD_FILES+=usr/share/man/man1/praudit.1.gz OLD_FILES+=usr/share/man/man2/audit.2.gz OLD_FILES+=usr/share/man/man2/auditctl.2.gz OLD_FILES+=usr/share/man/man2/auditon.2.gz OLD_FILES+=usr/share/man/man2/getaudit.2.gz OLD_FILES+=usr/share/man/man2/getaudit_addr.2.gz OLD_FILES+=usr/share/man/man2/getauid.2.gz OLD_FILES+=usr/share/man/man2/setaudit.2.gz OLD_FILES+=usr/share/man/man2/setaudit_addr.2.gz OLD_FILES+=usr/share/man/man2/setauid.2.gz OLD_FILES+=usr/share/man/man3/au_bsm_to_domain.3.gz OLD_FILES+=usr/share/man/man3/au_bsm_to_errno.3.gz OLD_FILES+=usr/share/man/man3/au_bsm_to_fcntl_cmd.3.gz OLD_FILES+=usr/share/man/man3/au_bsm_to_socket_type.3.gz OLD_FILES+=usr/share/man/man3/au_class.3.gz OLD_FILES+=usr/share/man/man3/au_close.3.gz OLD_FILES+=usr/share/man/man3/au_close_buffer.3.gz OLD_FILES+=usr/share/man/man3/au_close_token.3.gz OLD_FILES+=usr/share/man/man3/au_control.3.gz OLD_FILES+=usr/share/man/man3/au_domain.3.gz OLD_FILES+=usr/share/man/man3/au_domain_to_bsm.3.gz OLD_FILES+=usr/share/man/man3/au_errno.3.gz OLD_FILES+=usr/share/man/man3/au_errno_to_bsm.3.gz OLD_FILES+=usr/share/man/man3/au_event.3.gz OLD_FILES+=usr/share/man/man3/au_fcntl_cmd.3.gz OLD_FILES+=usr/share/man/man3/au_fcntl_cmd_t_bsm.3.gz OLD_FILES+=usr/share/man/man3/au_fetch_tok.3.gz OLD_FILES+=usr/share/man/man3/au_free_token.3.gz OLD_FILES+=usr/share/man/man3/au_io.3.gz OLD_FILES+=usr/share/man/man3/au_mask.3.gz OLD_FILES+=usr/share/man/man3/au_open.3.gz OLD_FILES+=usr/share/man/man3/au_poltostr.3.gz OLD_FILES+=usr/share/man/man3/au_preselect.3.gz OLD_FILES+=usr/share/man/man3/au_print_tok.3.gz OLD_FILES+=usr/share/man/man3/au_read_rec.3.gz OLD_FILES+=usr/share/man/man3/au_socket_type.3.gz OLD_FILES+=usr/share/man/man3/au_socket_type_to_bsm.3.gz OLD_FILES+=usr/share/man/man3/au_strerror.3.gz OLD_FILES+=usr/share/man/man3/au_strtopol.3.gz OLD_FILES+=usr/share/man/man3/au_to_arg.3.gz OLD_FILES+=usr/share/man/man3/au_to_arg32.3.gz OLD_FILES+=usr/share/man/man3/au_to_arg64.3.gz OLD_FILES+=usr/share/man/man3/au_to_attr64.3.gz OLD_FILES+=usr/share/man/man3/au_to_data.3.gz OLD_FILES+=usr/share/man/man3/au_to_exec_args.3.gz OLD_FILES+=usr/share/man/man3/au_to_exec_env.3.gz OLD_FILES+=usr/share/man/man3/au_to_exit.3.gz OLD_FILES+=usr/share/man/man3/au_to_file.3.gz OLD_FILES+=usr/share/man/man3/au_to_groups.3.gz OLD_FILES+=usr/share/man/man3/au_to_header.3.gz OLD_FILES+=usr/share/man/man3/au_to_header32.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject32.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject32_ex.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject64.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject64_ex.3.gz OLD_FILES+=usr/share/man/man3/au_to_subject_ex.3.gz OLD_FILES+=usr/share/man/man3/au_to_text.3.gz OLD_FILES+=usr/share/man/man3/au_to_trailer.3.gz OLD_FILES+=usr/share/man/man3/au_to_zonename.3.gz OLD_FILES+=usr/share/man/man3/au_token.3.gz OLD_FILES+=usr/share/man/man3/au_user.3.gz OLD_FILES+=usr/share/man/man3/au_user_mask.3.gz OLD_FILES+=usr/share/man/man3/au_write.3.gz OLD_FILES+=usr/share/man/man3/audit_submit.3.gz OLD_FILES+=usr/share/man/man3/bsm.3.gz OLD_FILES+=usr/share/man/man3/endac.3.gz OLD_FILES+=usr/share/man/man3/endauclass.3.gz OLD_FILES+=usr/share/man/man3/endauevent.3.gz OLD_FILES+=usr/share/man/man3/endauuser.3.gz OLD_FILES+=usr/share/man/man3/getacdir.3.gz OLD_FILES+=usr/share/man/man3/getacfilesz.3.gz OLD_FILES+=usr/share/man/man3/getacflg.3.gz OLD_FILES+=usr/share/man/man3/getacmin.3.gz OLD_FILES+=usr/share/man/man3/getacna.3.gz OLD_FILES+=usr/share/man/man3/getacpol.3.gz OLD_FILES+=usr/share/man/man3/getauclassent.3.gz OLD_FILES+=usr/share/man/man3/getauclassent_r.3.gz OLD_FILES+=usr/share/man/man3/getauclassnam.3.gz OLD_FILES+=usr/share/man/man3/getauclassnam_3.3.gz OLD_FILES+=usr/share/man/man3/getauditflagsbin.3.gz OLD_FILES+=usr/share/man/man3/getauditflagschar.3.gz OLD_FILES+=usr/share/man/man3/getauevent.3.gz OLD_FILES+=usr/share/man/man3/getauevent_r.3.gz OLD_FILES+=usr/share/man/man3/getauevnam.3.gz OLD_FILES+=usr/share/man/man3/getauevnam_r.3.gz OLD_FILES+=usr/share/man/man3/getauevnonam.3.gz OLD_FILES+=usr/share/man/man3/getauevnonam_r.3.gz OLD_FILES+=usr/share/man/man3/getauevnum.3.gz OLD_FILES+=usr/share/man/man3/getauevnum_r.3.gz OLD_FILES+=usr/share/man/man3/getauuserent.3.gz OLD_FILES+=usr/share/man/man3/getauuserent_r.3.gz OLD_FILES+=usr/share/man/man3/getauusernam.3.gz OLD_FILES+=usr/share/man/man3/getauusernam_R.3.gz OLD_FILES+=usr/share/man/man3/getfauditflags.3.gz OLD_FILES+=usr/share/man/man3/libbsm.3.gz OLD_FILES+=usr/share/man/man3/setac.3.gz OLD_FILES+=usr/share/man/man3/setauclass.3.gz OLD_FILES+=usr/share/man/man3/setauevent.3.gz OLD_FILES+=usr/share/man/man3/setauuser.3.gz OLD_FILES+=usr/share/man/man5/audit.log.5.gz OLD_FILES+=usr/share/man/man5/audit_class.5.gz OLD_FILES+=usr/share/man/man5/audit_control.5.gz OLD_FILES+=usr/share/man/man5/audit_event.5.gz OLD_FILES+=usr/share/man/man5/audit_user.5.gz OLD_FILES+=usr/share/man/man5/audit_warn.5.gz OLD_FILES+=usr/share/man/man8/audit.8.gz OLD_FILES+=usr/share/man/man8/auditd.8.gz OLD_DIRS+=etc/security # XXX: needs refactoring in sys/sys before it can be removed #OLD_DIRS+=usr/include/bsm .endif .if ${MK_AUTHPF} == no OLD_FILES+=usr/sbin/authpf OLD_FILES+=usr/sbin/authpf-noip OLD_FILES+=usr/share/man/man8/authpf.8.gz OLD_FILES+=usr/share/man/man8/authpf-noip.8.gz .endif .if ${MK_AUTOFS} == no OLD_FILES+=etc/autofs/include_ldap OLD_FILES+=etc/autofs/special_hosts OLD_FILES+=etc/autofs/special_media OLD_FILES+=etc/autofs/special_null OLD_FILES+=etc/auto_master OLD_FILES+=usr/sbin/automount OLD_FILES+=usr/sbin/automountd OLD_FILES+=usr/sbin/autounmountd OLD_FILES+=usr/share/man/man5/autofs.5.gz OLD_FILES+=usr/share/man/man5/auto_master.5.gz OLD_FILES+=usr/share/man/man8/automount.8.gz OLD_FILES+=usr/share/man/man8/automountd.8.gz OLD_FILES+=usr/share/man/man8/autounmountd.8.gz OLD_DIRS+=etc/autofs .endif .if ${MK_BHYVE} == no OLD_FILES+=usr/sbin/bhyve OLD_FILES+=usr/sbin/bhyvectl OLD_FILES+=usr/sbin/bhyveload OLD_FILES+=usr/share/examples/bhyve/vmrun.sh OLD_FILES+=usr/share/man/man8/bhyve.8.gz OLD_FILES+=usr/share/man/man8/bhyveload.8.gz OLD_DIRS+=usr/share/examples/bhyve .endif .if ${MK_BINUTILS} == no OLD_FILES+=usr/bin/as OLD_FILES+=usr/bin/ld OLD_FILES+=usr/bin/objcopy OLD_FILES+=usr/bin/objdump OLD_FILES+=usr/bin/readelf OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.x OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xbn OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xc OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xd OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xdc OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xdw OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xn OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xr OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xs OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xsc OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xsw OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xu OLD_FILES+=usr/libdata/ldscripts/elf_x86_64_fbsd.xw OLD_FILES+=usr/share/doc/binutils/as.txt OLD_FILES+=usr/share/doc/binutils/ld.txt OLD_FILES+=usr/share/man/man1/as.1.gz OLD_FILES+=usr/share/man/man1/ld.1.gz OLD_FILES+=usr/share/man/man1/objcopy.1.gz OLD_FILES+=usr/share/man/man1/objdump.1.gz OLD_FILES+=usr/share/man/man1/readelf.1.gz .endif .if ${MK_BLUETOOTH} == no OLD_FILES+=etc/bluetooth/hcsecd.conf OLD_FILES+=etc/bluetooth/hosts OLD_FILES+=etc/bluetooth/protocols OLD_FILES+=etc/defaults/bluetooth.device.conf OLD_DIRS+=etc/bluetooth OLD_FILES+=etc/rc.d/bluetooth OLD_FILES+=etc/rc.d/bthidd OLD_FILES+=etc/rc.d/hcsecd OLD_FILES+=etc/rc.d/sdpd OLD_FILES+=etc/rc.d/ubthidhci OLD_FILES+=usr/bin/bthost OLD_FILES+=usr/bin/btsockstat OLD_FILES+=usr/bin/rfcomm_sppd OLD_FILES+=usr/include/bluetooth.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_bluetooth.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_bt3c.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_btsocket.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_btsocket_hci_raw.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_btsocket_l2cap.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_btsocket_rfcomm.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_btsocket_sco.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_h4.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_hci.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_l2cap.h OLD_FILES+=usr/include/netgraph/bluetooth/include/ng_ubt.h OLD_DIRS+=usr/include/netgraph/bluetooth/include OLD_DIRS+=usr/include/netgraph/bluetooth OLD_FILES+=usr/include/sdp.h OLD_FILES+=usr/lib/libbluetooth.a OLD_FILES+=usr/lib/libbluetooth.so OLD_LIBS+=usr/lib/libbluetooth.so.4 OLD_FILES+=usr/lib/libbluetooth_p.a OLD_FILES+=usr/lib/libsdp.a OLD_FILES+=usr/lib/libsdp.so OLD_LIBS+=usr/lib/libsdp.so.4 OLD_FILES+=usr/lib/libsdp_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libbluetooth.a OLD_FILES+=usr/lib32/libbluetooth.so OLD_LIBS+=usr/lib32/libbluetooth.so.4 OLD_FILES+=usr/lib32/libbluetooth_p.a OLD_FILES+=usr/lib32/libsdp.a OLD_FILES+=usr/lib32/libsdp.so OLD_LIBS+=usr/lib32/libsdp.so.4 OLD_FILES+=usr/lib32/libsdp_p.a .endif OLD_FILES+=usr/sbin/ath3kfw OLD_FILES+=usr/sbin/bcmfw OLD_FILES+=usr/sbin/bt3cfw OLD_FILES+=usr/sbin/bthidcontrol OLD_FILES+=usr/sbin/bthidd OLD_FILES+=usr/sbin/btpand OLD_FILES+=usr/sbin/hccontrol OLD_FILES+=usr/sbin/hcsecd OLD_FILES+=usr/sbin/hcseriald OLD_FILES+=usr/sbin/l2control OLD_FILES+=usr/sbin/l2ping OLD_FILES+=usr/sbin/rfcomm_pppd OLD_FILES+=usr/sbin/sdpcontrol OLD_FILES+=usr/sbin/sdpd OLD_FILES+=usr/share/examples/etc/defaults/bluetooth.device.conf OLD_FILES+=usr/share/man/man1/bthost.1.gz OLD_FILES+=usr/share/man/man1/btsockstat.1.gz OLD_FILES+=usr/share/man/man1/rfcomm_sppd.1.gz OLD_FILES+=usr/share/man/man3/SDP_GET128.3.gz OLD_FILES+=usr/share/man/man3/SDP_GET16.3.gz OLD_FILES+=usr/share/man/man3/SDP_GET32.3.gz OLD_FILES+=usr/share/man/man3/SDP_GET64.3.gz OLD_FILES+=usr/share/man/man3/SDP_GET8.3.gz OLD_FILES+=usr/share/man/man3/SDP_PUT128.3.gz OLD_FILES+=usr/share/man/man3/SDP_PUT16.3.gz OLD_FILES+=usr/share/man/man3/SDP_PUT32.3.gz OLD_FILES+=usr/share/man/man3/SDP_PUT64.3.gz OLD_FILES+=usr/share/man/man3/SDP_PUT8.3.gz OLD_FILES+=usr/share/man/man3/bdaddr_any.3.gz OLD_FILES+=usr/share/man/man3/bdaddr_copy.3.gz OLD_FILES+=usr/share/man/man3/bdaddr_same.3.gz OLD_FILES+=usr/share/man/man3/bluetooth.3.gz OLD_FILES+=usr/share/man/man3/bt_aton.3.gz OLD_FILES+=usr/share/man/man3/bt_devaddr.3.gz OLD_FILES+=usr/share/man/man3/bt_devclose.3.gz OLD_FILES+=usr/share/man/man3/bt_devenum.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_evt_clr.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_evt_set.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_evt_tst.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_pkt_clr.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_pkt_set.3.gz OLD_FILES+=usr/share/man/man3/bt_devfilter_pkt_tst.3.gz OLD_FILES+=usr/share/man/man3/bt_devinfo.3.gz OLD_FILES+=usr/share/man/man3/bt_devinquiry.3.gz OLD_FILES+=usr/share/man/man3/bt_devname.3.gz OLD_FILES+=usr/share/man/man3/bt_devopen.3.gz OLD_FILES+=usr/share/man/man3/bt_devreq.3.gz OLD_FILES+=usr/share/man/man3/bt_devsend.3.gz OLD_FILES+=usr/share/man/man3/bt_endhostent.3.gz OLD_FILES+=usr/share/man/man3/bt_endprotoent.3.gz OLD_FILES+=usr/share/man/man3/bt_gethostbyaddr.3.gz OLD_FILES+=usr/share/man/man3/bt_gethostbyname.3.gz OLD_FILES+=usr/share/man/man3/bt_gethostent.3.gz OLD_FILES+=usr/share/man/man3/bt_getprotobyname.3.gz OLD_FILES+=usr/share/man/man3/bt_getprotobynumber.3.gz OLD_FILES+=usr/share/man/man3/bt_getprotoent.3.gz OLD_FILES+=usr/share/man/man3/bt_ntoa.3.gz OLD_FILES+=usr/share/man/man3/bt_sethostent.3.gz OLD_FILES+=usr/share/man/man3/bt_setprotoent.3.gz OLD_FILES+=usr/share/man/man3/sdp.3.gz OLD_FILES+=usr/share/man/man3/sdp_attr2desc.3.gz OLD_FILES+=usr/share/man/man3/sdp_change_service.3.gz OLD_FILES+=usr/share/man/man3/sdp_close.3.gz OLD_FILES+=usr/share/man/man3/sdp_error.3.gz OLD_FILES+=usr/share/man/man3/sdp_open.3.gz OLD_FILES+=usr/share/man/man3/sdp_open_local.3.gz OLD_FILES+=usr/share/man/man3/sdp_register_service.3.gz OLD_FILES+=usr/share/man/man3/sdp_search.3.gz OLD_FILES+=usr/share/man/man3/sdp_unregister_service.3.gz OLD_FILES+=usr/share/man/man3/sdp_uuid2desc.3.gz OLD_FILES+=usr/share/man/man5/hcsecd.conf.5.gz OLD_FILES+=usr/share/man/man8/ath3kfw.8.gz OLD_FILES+=usr/share/man/man8/bcmfw.8.gz OLD_FILES+=usr/share/man/man8/bt3cfw.8.gz OLD_FILES+=usr/share/man/man8/bthidcontrol.8.gz OLD_FILES+=usr/share/man/man8/bthidd.8.gz OLD_FILES+=usr/share/man/man8/btpand.8.gz OLD_FILES+=usr/share/man/man8/hccontrol.8.gz OLD_FILES+=usr/share/man/man8/hcsecd.8.gz OLD_FILES+=usr/share/man/man8/hcseriald.8.gz OLD_FILES+=usr/share/man/man8/l2control.8.gz OLD_FILES+=usr/share/man/man8/l2ping.8.gz OLD_FILES+=usr/share/man/man8/rfcomm_pppd.8.gz OLD_FILES+=usr/share/man/man8/sdpcontrol.8.gz OLD_FILES+=usr/share/man/man8/sdpd.8.gz .endif .if ${MK_BOOT} == no OLD_FILES+=boot/beastie.4th OLD_FILES+=boot/boot OLD_FILES+=boot/boot0 OLD_FILES+=boot/boot0sio OLD_FILES+=boot/boot1 OLD_FILES+=boot/boot1.efi OLD_FILES+=boot/boot1.efifat OLD_FILES+=boot/boot2 OLD_FILES+=boot/brand.4th OLD_FILES+=boot/cdboot OLD_FILES+=boot/check-password.4th OLD_FILES+=boot/color.4th OLD_FILES+=boot/defaults/loader.conf OLD_FILES+=boot/delay.4th OLD_FILES+=boot/device.hints OLD_FILES+=boot/frames.4th OLD_FILES+=boot/gptboot OLD_FILES+=boot/gptzfsboot OLD_FILES+=boot/loader OLD_FILES+=boot/loader.4th OLD_FILES+=boot/loader.efi OLD_FILES+=boot/loader.help OLD_FILES+=boot/loader.rc OLD_FILES+=boot/mbr OLD_FILES+=boot/menu-commands.4th OLD_FILES+=boot/menu.4th OLD_FILES+=boot/menu.rc OLD_FILES+=boot/menusets.4th OLD_FILES+=boot/pcibios.4th OLD_FILES+=boot/pmbr OLD_FILES+=boot/pxeboot OLD_FILES+=boot/screen.4th OLD_FILES+=boot/shortcuts.4th OLD_FILES+=boot/support.4th OLD_FILES+=boot/userboot.so OLD_FILES+=boot/version.4th OLD_FILES+=boot/zfsboot OLD_FILES+=boot/zfsloader OLD_FILES+=etc/rc.d/gptboot OLD_FILES+=usr/lib/kgzldr.o OLD_FILES+=usr/share/man/man5/loader.conf.5.gz OLD_FILES+=usr/share/man/man8/beastie.4th.8.gz OLD_FILES+=usr/share/man/man8/brand.4th.8.gz OLD_FILES+=usr/share/man/man8/check-password.4th.8.gz OLD_FILES+=usr/share/man/man8/color.4th.8.gz OLD_FILES+=usr/share/man/man8/delay.4th.8.gz OLD_FILES+=usr/share/man/man8/gptboot.8.gz OLD_FILES+=usr/share/man/man8/gptzfsboot.8.gz OLD_FILES+=usr/share/man/man8/loader.4th.8.gz OLD_FILES+=usr/share/man/man8/loader.8.gz OLD_FILES+=usr/share/man/man8/menu.4th.8.gz OLD_FILES+=usr/share/man/man8/menusets.4th.8.gz OLD_FILES+=usr/share/man/man8/pxeboot.8.gz OLD_FILES+=usr/share/man/man8/version.4th.8.gz OLD_FILES+=usr/share/man/man8/zfsboot.8.gz OLD_FILES+=usr/share/man/man8/zfsloader.8.gz OLD_DIRS+=boot/defaults .endif .if ${MK_BOOTPARAMD} == no OLD_FILES+=rc.d/bootparams OLD_FILES+=usr/sbin/bootparamd OLD_FILES+=usr/sbin/callbootd OLD_FILES+=usr/share/man/man5/bootparams.5.gz OLD_FILES+=usr/share/man/man8/bootparamd.8.gz .endif .if ${MK_BOOTPD} == no OLD_FILES+=usr/libexec/bootpd OLD_FILES+=usr/libexec/bootpgw OLD_FILES+=usr/sbin/bootpef OLD_FILES+=usr/sbin/bootptest OLD_FILES+=usr/share/man/man5/bootptab.5.gz OLD_FILES+=usr/share/man/man8/bootpd.8.gz OLD_FILES+=usr/share/man/man8/bootpef.8.gz OLD_FILES+=usr/share/man/man8/bootpgw.8.gz OLD_FILES+=usr/share/man/man8/bootptest.8.gz .endif .if ${MK_BSDCONFIG} == no OLD_FILES+=usr/libexec/bsdconfig/020.docsinstall/INDEX OLD_FILES+=usr/libexec/bsdconfig/020.docsinstall/USAGE OLD_FILES+=usr/libexec/bsdconfig/020.docsinstall/docsinstall OLD_FILES+=usr/libexec/bsdconfig/020.docsinstall/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/030.packages/INDEX OLD_FILES+=usr/libexec/bsdconfig/030.packages/USAGE OLD_FILES+=usr/libexec/bsdconfig/030.packages/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/030.packages/packages OLD_FILES+=usr/libexec/bsdconfig/040.password/INDEX OLD_FILES+=usr/libexec/bsdconfig/040.password/USAGE OLD_FILES+=usr/libexec/bsdconfig/040.password/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/040.password/password OLD_FILES+=usr/libexec/bsdconfig/050.diskmgmt/INDEX OLD_FILES+=usr/libexec/bsdconfig/050.diskmgmt/USAGE OLD_FILES+=usr/libexec/bsdconfig/050.diskmgmt/diskmgmt OLD_FILES+=usr/libexec/bsdconfig/050.diskmgmt/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/INDEX OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/USAGE OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/groupadd OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/groupdel OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/groupedit OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/include/usermgmt.hlp OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/useradd OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/userdel OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/useredit OLD_FILES+=usr/libexec/bsdconfig/070.usermgmt/usermgmt OLD_FILES+=usr/libexec/bsdconfig/080.console/INDEX OLD_FILES+=usr/libexec/bsdconfig/080.console/USAGE OLD_FILES+=usr/libexec/bsdconfig/080.console/console OLD_FILES+=usr/libexec/bsdconfig/080.console/font OLD_FILES+=usr/libexec/bsdconfig/080.console/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/080.console/keymap OLD_FILES+=usr/libexec/bsdconfig/080.console/repeat OLD_FILES+=usr/libexec/bsdconfig/080.console/saver OLD_FILES+=usr/libexec/bsdconfig/080.console/screenmap OLD_FILES+=usr/libexec/bsdconfig/080.console/ttys OLD_FILES+=usr/libexec/bsdconfig/090.timezone/INDEX OLD_FILES+=usr/libexec/bsdconfig/090.timezone/USAGE OLD_FILES+=usr/libexec/bsdconfig/090.timezone/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/090.timezone/timezone OLD_FILES+=usr/libexec/bsdconfig/110.mouse/INDEX OLD_FILES+=usr/libexec/bsdconfig/110.mouse/USAGE OLD_FILES+=usr/libexec/bsdconfig/110.mouse/disable OLD_FILES+=usr/libexec/bsdconfig/110.mouse/enable OLD_FILES+=usr/libexec/bsdconfig/110.mouse/flags OLD_FILES+=usr/libexec/bsdconfig/110.mouse/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/110.mouse/mouse OLD_FILES+=usr/libexec/bsdconfig/110.mouse/port OLD_FILES+=usr/libexec/bsdconfig/110.mouse/type OLD_FILES+=usr/libexec/bsdconfig/120.networking/INDEX OLD_FILES+=usr/libexec/bsdconfig/120.networking/USAGE OLD_FILES+=usr/libexec/bsdconfig/120.networking/defaultrouter OLD_FILES+=usr/libexec/bsdconfig/120.networking/devices OLD_FILES+=usr/libexec/bsdconfig/120.networking/hostname OLD_FILES+=usr/libexec/bsdconfig/120.networking/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/120.networking/nameservers OLD_FILES+=usr/libexec/bsdconfig/120.networking/networking OLD_FILES+=usr/libexec/bsdconfig/130.security/INDEX OLD_FILES+=usr/libexec/bsdconfig/130.security/USAGE OLD_FILES+=usr/libexec/bsdconfig/130.security/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/130.security/include/securelevel.hlp OLD_FILES+=usr/libexec/bsdconfig/130.security/kern_securelevel OLD_FILES+=usr/libexec/bsdconfig/130.security/security OLD_FILES+=usr/libexec/bsdconfig/140.startup/INDEX OLD_FILES+=usr/libexec/bsdconfig/140.startup/USAGE OLD_FILES+=usr/libexec/bsdconfig/140.startup/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/140.startup/misc OLD_FILES+=usr/libexec/bsdconfig/140.startup/rcadd OLD_FILES+=usr/libexec/bsdconfig/140.startup/rcconf OLD_FILES+=usr/libexec/bsdconfig/140.startup/rcdelete OLD_FILES+=usr/libexec/bsdconfig/140.startup/rcedit OLD_FILES+=usr/libexec/bsdconfig/140.startup/rcvar OLD_FILES+=usr/libexec/bsdconfig/140.startup/startup OLD_FILES+=usr/libexec/bsdconfig/150.ttys/INDEX OLD_FILES+=usr/libexec/bsdconfig/150.ttys/USAGE OLD_FILES+=usr/libexec/bsdconfig/150.ttys/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/150.ttys/ttys OLD_FILES+=usr/libexec/bsdconfig/USAGE OLD_FILES+=usr/libexec/bsdconfig/dot/INDEX OLD_FILES+=usr/libexec/bsdconfig/dot/USAGE OLD_FILES+=usr/libexec/bsdconfig/dot/dot OLD_FILES+=usr/libexec/bsdconfig/dot/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/include/bsdconfig.hlp OLD_FILES+=usr/libexec/bsdconfig/include/media.hlp OLD_FILES+=usr/libexec/bsdconfig/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/include/network_device.hlp OLD_FILES+=usr/libexec/bsdconfig/include/options.hlp OLD_FILES+=usr/libexec/bsdconfig/include/tcp.hlp OLD_FILES+=usr/libexec/bsdconfig/include/usage.hlp OLD_FILES+=usr/libexec/bsdconfig/includes/INDEX OLD_FILES+=usr/libexec/bsdconfig/includes/USAGE OLD_FILES+=usr/libexec/bsdconfig/includes/include/messages.subr OLD_FILES+=usr/libexec/bsdconfig/includes/includes OLD_FILES+=usr/sbin/bsdconfig OLD_FILES+=usr/sbin/sysrc OLD_FILES+=usr/share/bsdconfig/common.subr OLD_FILES+=usr/share/bsdconfig/device.subr OLD_FILES+=usr/share/bsdconfig/dialog.subr OLD_FILES+=usr/share/bsdconfig/geom.subr OLD_FILES+=usr/share/bsdconfig/keymap.subr OLD_FILES+=usr/share/bsdconfig/media/any.subr OLD_FILES+=usr/share/bsdconfig/media/cdrom.subr OLD_FILES+=usr/share/bsdconfig/media/common.subr OLD_FILES+=usr/share/bsdconfig/media/directory.subr OLD_FILES+=usr/share/bsdconfig/media/dos.subr OLD_FILES+=usr/share/bsdconfig/media/floppy.subr OLD_FILES+=usr/share/bsdconfig/media/ftp.subr OLD_FILES+=usr/share/bsdconfig/media/http.subr OLD_FILES+=usr/share/bsdconfig/media/httpproxy.subr OLD_FILES+=usr/share/bsdconfig/media/network.subr OLD_FILES+=usr/share/bsdconfig/media/nfs.subr OLD_FILES+=usr/share/bsdconfig/media/options.subr OLD_FILES+=usr/share/bsdconfig/media/tcpip.subr OLD_FILES+=usr/share/bsdconfig/media/ufs.subr OLD_FILES+=usr/share/bsdconfig/media/usb.subr OLD_FILES+=usr/share/bsdconfig/mustberoot.subr OLD_FILES+=usr/share/bsdconfig/networking/common.subr OLD_FILES+=usr/share/bsdconfig/networking/device.subr OLD_FILES+=usr/share/bsdconfig/networking/hostname.subr OLD_FILES+=usr/share/bsdconfig/networking/ipaddr.subr OLD_FILES+=usr/share/bsdconfig/networking/media.subr OLD_FILES+=usr/share/bsdconfig/networking/netmask.subr OLD_FILES+=usr/share/bsdconfig/networking/resolv.subr OLD_FILES+=usr/share/bsdconfig/networking/routing.subr OLD_FILES+=usr/share/bsdconfig/networking/services.subr OLD_FILES+=usr/share/bsdconfig/packages/categories.subr OLD_FILES+=usr/share/bsdconfig/packages/index.subr OLD_FILES+=usr/share/bsdconfig/packages/musthavepkg.subr OLD_FILES+=usr/share/bsdconfig/packages/packages.subr OLD_FILES+=usr/share/bsdconfig/password/password.subr OLD_FILES+=usr/share/bsdconfig/script.subr OLD_FILES+=usr/share/bsdconfig/startup/rcconf.subr OLD_FILES+=usr/share/bsdconfig/startup/rcedit.subr OLD_FILES+=usr/share/bsdconfig/startup/rcvar.subr OLD_FILES+=usr/share/bsdconfig/strings.subr OLD_FILES+=usr/share/bsdconfig/struct.subr OLD_FILES+=usr/share/bsdconfig/sysrc.subr OLD_FILES+=usr/share/bsdconfig/timezone/continents.subr OLD_FILES+=usr/share/bsdconfig/timezone/countries.subr OLD_FILES+=usr/share/bsdconfig/timezone/iso3166.subr OLD_FILES+=usr/share/bsdconfig/timezone/menus.subr OLD_FILES+=usr/share/bsdconfig/timezone/zones.subr OLD_FILES+=usr/share/bsdconfig/usermgmt/group.subr OLD_FILES+=usr/share/bsdconfig/usermgmt/group_input.subr OLD_FILES+=usr/share/bsdconfig/usermgmt/user.subr OLD_FILES+=usr/share/bsdconfig/usermgmt/user_input.subr OLD_FILES+=usr/share/bsdconfig/variable.subr OLD_FILES+=usr/share/examples/bsdconfig/add_some_packages.sh OLD_FILES+=usr/share/examples/bsdconfig/browse_packages_http.sh OLD_FILES+=usr/share/examples/bsdconfig/bsdconfigrc OLD_FILES+=usr/share/man/man8/bsdconfig.8.gz OLD_FILES+=usr/share/man/man8/sysrc.8.gz OLD_DIRS+=usr/libexec/bsdconfig/020.docsinstall/include OLD_DIRS+=usr/libexec/bsdconfig/020.docsinstall OLD_DIRS+=usr/libexec/bsdconfig/030.packages/include OLD_DIRS+=usr/libexec/bsdconfig/030.packages OLD_DIRS+=usr/libexec/bsdconfig/040.password/include OLD_DIRS+=usr/libexec/bsdconfig/040.password OLD_DIRS+=usr/libexec/bsdconfig/050.diskmgmt/include OLD_DIRS+=usr/libexec/bsdconfig/050.diskmgmt OLD_DIRS+=usr/libexec/bsdconfig/070.usermgmt/include OLD_DIRS+=usr/libexec/bsdconfig/070.usermgmt OLD_DIRS+=usr/libexec/bsdconfig/080.console/include OLD_DIRS+=usr/libexec/bsdconfig/080.console OLD_DIRS+=usr/libexec/bsdconfig/090.timezone/include OLD_DIRS+=usr/libexec/bsdconfig/090.timezone OLD_DIRS+=usr/libexec/bsdconfig/110.mouse/include OLD_DIRS+=usr/libexec/bsdconfig/110.mouse OLD_DIRS+=usr/libexec/bsdconfig/120.networking/include OLD_DIRS+=usr/libexec/bsdconfig/120.networking OLD_DIRS+=usr/libexec/bsdconfig/130.security/include OLD_DIRS+=usr/libexec/bsdconfig/130.security OLD_DIRS+=usr/libexec/bsdconfig/140.startup/include OLD_DIRS+=usr/libexec/bsdconfig/140.startup OLD_DIRS+=usr/libexec/bsdconfig/150.ttys/include OLD_DIRS+=usr/libexec/bsdconfig/150.ttys OLD_DIRS+=usr/libexec/bsdconfig/dot/include OLD_DIRS+=usr/libexec/bsdconfig/dot OLD_DIRS+=usr/libexec/bsdconfig/include OLD_DIRS+=usr/libexec/bsdconfig/includes/include OLD_DIRS+=usr/libexec/bsdconfig/includes OLD_DIRS+=usr/libexec/bsdconfig OLD_DIRS+=usr/share/bsdconfig/media OLD_DIRS+=usr/share/bsdconfig/networking OLD_DIRS+=usr/share/bsdconfig/packages OLD_DIRS+=usr/share/bsdconfig/password OLD_DIRS+=usr/share/bsdconfig/startup OLD_DIRS+=usr/share/bsdconfig/timezone OLD_DIRS+=usr/share/bsdconfig/usermgmt OLD_DIRS+=usr/share/bsdconfig OLD_DIRS+=usr/share/examples/bsdconfig .endif .if ${MK_BSD_CPIO} == no OLD_FILES+=usr/bin/bsdcpio OLD_FILES+=usr/bin/cpio OLD_FILES+=usr/share/man/man1/bsdcpio.1.gz OLD_FILES+=usr/share/man/man1/cpio.1.gz .endif .if ${MK_BSDINSTALL} == no OLD_FILES+=usr/libexec/bsdinstall/adduser OLD_FILES+=usr/libexec/bsdinstall/auto OLD_FILES+=usr/libexec/bsdinstall/autopart OLD_FILES+=usr/libexec/bsdinstall/checksum OLD_FILES+=usr/libexec/bsdinstall/config OLD_FILES+=usr/libexec/bsdinstall/distextract OLD_FILES+=usr/libexec/bsdinstall/distfetch OLD_FILES+=usr/libexec/bsdinstall/docsinstall OLD_FILES+=usr/libexec/bsdinstall/entropy OLD_FILES+=usr/libexec/bsdinstall/hostname OLD_FILES+=usr/libexec/bsdinstall/jail OLD_FILES+=usr/libexec/bsdinstall/keymap OLD_FILES+=usr/libexec/bsdinstall/mirrorselect OLD_FILES+=usr/libexec/bsdinstall/mount OLD_FILES+=usr/libexec/bsdinstall/netconfig OLD_FILES+=usr/libexec/bsdinstall/netconfig_ipv4 OLD_FILES+=usr/libexec/bsdinstall/netconfig_ipv6 OLD_FILES+=usr/libexec/bsdinstall/partedit OLD_FILES+=usr/libexec/bsdinstall/rootpass OLD_FILES+=usr/libexec/bsdinstall/script OLD_FILES+=usr/libexec/bsdinstall/scriptedpart OLD_FILES+=usr/libexec/bsdinstall/services OLD_FILES+=usr/libexec/bsdinstall/time OLD_FILES+=usr/libexec/bsdinstall/umount OLD_FILES+=usr/libexec/bsdinstall/wlanconfig OLD_FILES+=usr/libexec/bsdinstall/zfsboot OLD_FILES+=usr/sbin/bsdinstall OLD_FILES+=usr/share/man/man8/bsdinstall.8.gz OLD_FILES+=usr/share/man/man8/sade.8.gz OLD_DIRS+=usr/libexec/bsdinstall .endif .if ${MK_BSNMP} == no OLD_FILES+=etc/snmpd.config OLD_FILES+=etc/rc.d/bsnmpd OLD_FILES+=usr/bin/bsnmpget OLD_FILES+=usr/bin/bsnmpset OLD_FILES+=usr/bin/bsnmpwalk OLD_FILES+=usr/include/bsnmp/asn1.h OLD_FILES+=usr/include/bsnmp/bridge_snmp.h OLD_FILES+=usr/include/bsnmp/snmp.h OLD_FILES+=usr/include/bsnmp/snmp_atm.h OLD_FILES+=usr/include/bsnmp/snmp_mibII.h OLD_FILES+=usr/include/bsnmp/snmp_netgraph.h OLD_FILES+=usr/include/bsnmp/snmpagent.h OLD_FILES+=usr/include/bsnmp/snmpclient.h OLD_FILES+=usr/include/bsnmp/snmpmod.h OLD_FILES+=usr/lib/libbsnmp.a OLD_FILES+=usr/lib/libbsnmp.so OLD_LIBS+=usr/lib/libbsnmp.so.6 OLD_FILES+=usr/lib/libbsnmp_p.a OLD_FILES+=usr/lib/libbsnmptools.a OLD_FILES+=usr/lib/libbsnmptools.so OLD_LIBS+=usr/lib/libbsnmptools.so.0 OLD_FILES+=usr/lib/libbsnmptools_p.a OLD_FILES+=usr/lib/snmp_atm.so OLD_LIBS+=usr/lib/snmp_atm.so.6 OLD_FILES+=usr/lib/snmp_bridge.so OLD_LIBS+=usr/lib/snmp_bridge.so.6 OLD_FILES+=usr/lib/snmp_hast.so OLD_LIBS+=usr/lib/snmp_hast.so.6 OLD_FILES+=usr/lib/snmp_hostres.so OLD_LIBS+=usr/lib/snmp_hostres.so.6 OLD_FILES+=usr/lib/snmp_lm75.so OLD_LIBS+=usr/lib/snmp_lm75.so.6 OLD_FILES+=usr/lib/snmp_mibII.so OLD_LIBS+=usr/lib/snmp_mibII.so.6 OLD_FILES+=usr/lib/snmp_netgraph.so OLD_LIBS+=usr/lib/snmp_netgraph.so.6 OLD_FILES+=usr/lib/snmp_pf.so OLD_LIBS+=usr/lib/snmp_pf.so.6 OLD_FILES+=usr/lib/snmp_target.so OLD_LIBS+=usr/lib/snmp_target.so.6 OLD_FILES+=usr/lib/snmp_usm.so OLD_LIBS+=usr/lib/snmp_usm.so.6 OLD_FILES+=usr/lib/snmp_vacm.so OLD_LIBS+=usr/lib/snmp_vacm.so.6 OLD_FILES+=usr/lib/snmp_wlan.so OLD_LIBS+=usr/lib/snmp_wlan.so.6 OLD_FILES+=usr/lib32/libbsnmp.a OLD_FILES+=usr/lib32/libbsnmp.so OLD_LIBS+=usr/lib32/libbsnmp.so.6 OLD_FILES+=usr/lib32/libbsnmp_p.a OLD_FILES+=usr/sbin/bsnmpd OLD_FILES+=usr/sbin/gensnmptree OLD_FILES+=usr/share/examples/etc/snmpd.config OLD_FILES+=usr/share/man/man1/bsnmpd.1.gz OLD_FILES+=usr/share/man/man1/bsnmpget.1.gz OLD_FILES+=usr/share/man/man1/bsnmpset.1.gz OLD_FILES+=usr/share/man/man1/bsnmpwalk.1.gz OLD_FILES+=usr/share/man/man1/gensnmptree.1.gz OLD_FILES+=usr/share/man/man3/asn1.3.gz OLD_FILES+=usr/share/man/man3/bsnmpagent.3.gz OLD_FILES+=usr/share/man/man3/bsnmpclient.3.gz OLD_FILES+=usr/share/man/man3/bsnmplib.3.gz OLD_FILES+=usr/share/man/man3/snmp_atm.3.gz OLD_FILES+=usr/share/man/man3/snmp_bridge.3.gz OLD_FILES+=usr/share/man/man3/snmp_hast.3.gz OLD_FILES+=usr/share/man/man3/snmp_hostres.3.gz OLD_FILES+=usr/share/man/man3/snmp_lm75.3.gz OLD_FILES+=usr/share/man/man3/snmp_mibII.3.gz OLD_FILES+=usr/share/man/man3/snmp_netgraph.3.gz OLD_FILES+=usr/share/man/man3/snmp_target.3.gz OLD_FILES+=usr/share/man/man3/snmp_usm.3.gz OLD_FILES+=usr/share/man/man3/snmp_vacm.3.gz OLD_FILES+=usr/share/man/man3/snmp_wlan.3.gz OLD_FILES+=usr/share/man/man3/snmpmod.3.gz OLD_FILES+=usr/share/snmp/defs/atm_freebsd.def OLD_FILES+=usr/share/snmp/defs/atm_tree.def OLD_FILES+=usr/share/snmp/defs/bridge_tree.def OLD_FILES+=usr/share/snmp/defs/hast_tree.def OLD_FILES+=usr/share/snmp/defs/hostres_tree.def OLD_FILES+=usr/share/snmp/defs/lm75_tree.def OLD_FILES+=usr/share/snmp/defs/mibII_tree.def OLD_FILES+=usr/share/snmp/defs/netgraph_tree.def OLD_FILES+=usr/share/snmp/defs/pf_tree.def OLD_FILES+=usr/share/snmp/defs/target_tree.def OLD_FILES+=usr/share/snmp/defs/tree.def OLD_FILES+=usr/share/snmp/defs/usm_tree.def OLD_FILES+=usr/share/snmp/defs/vacm_tree.def OLD_FILES+=usr/share/snmp/defs/wlan_tree.def OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-ATM-FREEBSD-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-ATM.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-BRIDGE-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-HAST-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-HOSTRES-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-IP-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-LM75-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-MIB2-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-NETGRAPH.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-PF-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-SNMPD.txt OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-WIRELESS-MIB.txt OLD_FILES+=usr/share/snmp/mibs/BRIDGE-MIB.txt OLD_FILES+=usr/share/snmp/mibs/FOKUS-MIB.txt OLD_FILES+=usr/share/snmp/mibs/FREEBSD-MIB.txt OLD_FILES+=usr/share/snmp/mibs/RSTP-MIB.txt OLD_DIRS+=usr/include/bsnmp OLD_DIRS+=usr/share/snmp OLD_DIRS+=usr/share/snmp/defs OLD_DIRS+=usr/share/snmp/mibs .endif .if ${MK_CALENDAR} == no OLD_FILES+=etc/periodic/daily/300.calendar OLD_FILES+=usr/bin/calendar OLD_FILES+=usr/share/calendar/calendar.all OLD_FILES+=usr/share/calendar/calendar.australia OLD_FILES+=usr/share/calendar/calendar.birthday OLD_FILES+=usr/share/calendar/calendar.brazilian OLD_FILES+=usr/share/calendar/calendar.christian OLD_FILES+=usr/share/calendar/calendar.computer OLD_FILES+=usr/share/calendar/calendar.croatian OLD_FILES+=usr/share/calendar/calendar.dutch OLD_FILES+=usr/share/calendar/calendar.freebsd OLD_FILES+=usr/share/calendar/calendar.french OLD_FILES+=usr/share/calendar/calendar.german OLD_FILES+=usr/share/calendar/calendar.history OLD_FILES+=usr/share/calendar/calendar.holiday OLD_FILES+=usr/share/calendar/calendar.hungarian OLD_FILES+=usr/share/calendar/calendar.judaic OLD_FILES+=usr/share/calendar/calendar.lotr OLD_FILES+=usr/share/calendar/calendar.music OLD_FILES+=usr/share/calendar/calendar.newzealand OLD_FILES+=usr/share/calendar/calendar.russian OLD_FILES+=usr/share/calendar/calendar.southafrica OLD_FILES+=usr/share/calendar/calendar.ukrainian OLD_FILES+=usr/share/calendar/calendar.usholiday OLD_FILES+=usr/share/calendar/calendar.world OLD_FILES+=usr/share/calendar/de_AT.ISO_8859-15/calendar.feiertag OLD_DIRS+=usr/share/calendar/de_AT.ISO_8859-15 OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.all OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.feiertag OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.geschichte OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.kirche OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.literatur OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.musik OLD_FILES+=usr/share/calendar/de_DE.ISO8859-1/calendar.wissenschaft OLD_DIRS+=usr/share/calendar/de_DE.ISO8859-1 OLD_FILES+=usr/share/calendar/de_DE.ISO8859-15 OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-1/calendar.all OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-1/calendar.fetes OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-1/calendar.french OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-1/calendar.jferies OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-1/calendar.proverbes OLD_DIRS+=usr/share/calendar/fr_FR.ISO8859-1 OLD_FILES+=usr/share/calendar/fr_FR.ISO8859-15 OLD_FILES+=usr/share/calendar/hr_HR.ISO8859-2/calendar.all OLD_FILES+=usr/share/calendar/hr_HR.ISO8859-2/calendar.praznici OLD_DIRS+=usr/share/calendar/hr_HR.ISO8859-2 OLD_FILES+=usr/share/calendar/hu_HU.ISO8859-2/calendar.all OLD_FILES+=usr/share/calendar/hu_HU.ISO8859-2/calendar.nevnapok OLD_FILES+=usr/share/calendar/hu_HU.ISO8859-2/calendar.unnepek OLD_DIRS+=usr/share/calendar/hu_HU.ISO8859-2 OLD_FILES+=usr/share/calendar/pt_BR.ISO8859-1/calendar.all OLD_FILES+=usr/share/calendar/pt_BR.ISO8859-1/calendar.commemorative OLD_FILES+=usr/share/calendar/pt_BR.ISO8859-1/calendar.holidays OLD_FILES+=usr/share/calendar/pt_BR.ISO8859-1/calendar.mcommemorative OLD_DIRS+=usr/share/calendar/pt_BR.ISO8859-1 OLD_FILES+=usr/share/calendar/pt_BR.UTF-8/calendar.all OLD_FILES+=usr/share/calendar/pt_BR.UTF-8/calendar.commemorative OLD_FILES+=usr/share/calendar/pt_BR.UTF-8/calendar.holidays OLD_FILES+=usr/share/calendar/pt_BR.UTF-8/calendar.mcommemorative OLD_DIRS+=usr/share/calendar/pt_BR.UTF-8 OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.all OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.common OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.holiday OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.military OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.orthodox OLD_FILES+=usr/share/calendar/ru_RU.KOI8-R/calendar.pagan OLD_DIRS+=usr/share/calendar/ru_RU.KOI8-R OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.all OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.common OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.holiday OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.military OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.orthodox OLD_FILES+=usr/share/calendar/ru_RU.UTF-8/calendar.pagan OLD_DIRS+=usr/share/calendar/ru_RU.UTF-8 OLD_FILES+=usr/share/calendar/uk_UA.KOI8-U/calendar.all OLD_FILES+=usr/share/calendar/uk_UA.KOI8-U/calendar.holiday OLD_FILES+=usr/share/calendar/uk_UA.KOI8-U/calendar.misc OLD_FILES+=usr/share/calendar/uk_UA.KOI8-U/calendar.orthodox OLD_DIRS+=usr/share/calendar/uk_UA.KOI8-U OLD_DIRS+=usr/share/calendar OLD_FILES+=usr/share/man/man1/calendar.1.gz .endif .if ${MK_CASPER} == no OLD_FILES+=etc/rc.d/casperd OLD_LIBS+=lib/libcasper.so.0 OLD_FILES+=sbin/casper OLD_FILES+=usr/lib/libcasper.a .endif .if ${MK_CCD} == no OLD_FILES+=etc/rc.d/ccd OLD_FILES+=sbin/ccdconfig OLD_FILES+=usr/share/man/man4/ccd.4.gz OLD_FILES+=usr/share/man/man8/ccdconfig.8.gz .endif .if ${MK_CDDL} == no OLD_LIBS+=lib/libavl.so.2 OLD_LIBS+=lib/libctf.so.2 OLD_LIBS+=lib/libdtrace.so.2 OLD_LIBS+=lib/libnvpair.so.2 OLD_LIBS+=lib/libumem.so.2 OLD_LIBS+=lib/libuutil.so.2 OLD_FILES+=usr/bin/ctfconvert OLD_FILES+=usr/bin/ctfdump OLD_FILES+=usr/bin/ctfmerge OLD_FILES+=usr/bin/sgsmsg OLD_FILES+=usr/lib/dtrace/drti.o OLD_FILES+=usr/lib/dtrace/errno.d OLD_FILES+=usr/lib/dtrace/io.d OLD_FILES+=usr/lib/dtrace/ip.d OLD_FILES+=usr/lib/dtrace/psinfo.d .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" OLD_FILES+=usr/lib/dtrace/regs_x86.d .endif OLD_FILES+=usr/lib/dtrace/signal.d OLD_FILES+=usr/lib/dtrace/tcp.d OLD_FILES+=usr/lib/dtrace/udp.d OLD_FILES+=usr/lib/dtrace/unistd.d OLD_FILES+=usr/lib/libavl.a OLD_FILES+=usr/lib/libavl.so OLD_FILES+=usr/lib/libavl_p.a OLD_FILES+=usr/lib/libctf.a OLD_FILES+=usr/lib/libctf.so OLD_FILES+=usr/lib/libctf_p.a OLD_FILES+=usr/lib/libdtrace.a OLD_FILES+=usr/lib/libdtrace.so OLD_FILES+=usr/lib/libdtrace_p.a OLD_FILES+=usr/lib/libnvpair.a OLD_FILES+=usr/lib/libnvpair.so OLD_FILES+=usr/lib/libnvpair_p.a OLD_FILES+=usr/lib/libumem.a OLD_FILES+=usr/lib/libumem.so OLD_FILES+=usr/lib/libumem_p.a OLD_FILES+=usr/lib/libuutil.a OLD_FILES+=usr/lib/libuutil.so OLD_FILES+=usr/lib/libuutil_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/dtrace/drti.o OLD_FILES+=usr/lib32/libavl.a OLD_FILES+=usr/lib32/libavl.so OLD_LIBS+=usr/lib32/libavl.so.2 OLD_FILES+=usr/lib32/libavl_p.a OLD_FILES+=usr/lib32/libctf.a OLD_FILES+=usr/lib32/libctf.so OLD_LIBS+=usr/lib32/libctf.so.2 OLD_FILES+=usr/lib32/libctf_p.a OLD_FILES+=usr/lib32/libdtrace.a OLD_FILES+=usr/lib32/libdtrace.so OLD_LIBS+=usr/lib32/libdtrace.so.2 OLD_FILES+=usr/lib32/libdtrace_p.a OLD_FILES+=usr/lib32/libnvpair.a OLD_FILES+=usr/lib32/libnvpair.so OLD_LIBS+=usr/lib32/libnvpair.so.2 OLD_FILES+=usr/lib32/libnvpair_p.a OLD_FILES+=usr/lib32/libumem.a OLD_FILES+=usr/lib32/libumem.so OLD_LIBS+=usr/lib32/libumem.so.2 OLD_FILES+=usr/lib32/libumem_p.a OLD_FILES+=usr/lib32/libuutil.a OLD_FILES+=usr/lib32/libuutil.so OLD_LIBS+=usr/lib32/libuutil.so.2 OLD_FILES+=usr/lib32/libuutil_p.a .endif OLD_LIBS+=lib/libdtrace.so.2 OLD_FILES+=usr/sbin/dtrace OLD_FILES+=usr/sbin/lockstat OLD_FILES+=usr/share/man/man1/dtrace.1.gz OLD_FILES+=usr/share/dtrace/disklatency OLD_FILES+=usr/share/dtrace/disklatencycmd OLD_FILES+=usr/share/dtrace/hotopen OLD_FILES+=usr/share/dtrace/nfsclienttime OLD_FILES+=usr/share/dtrace/toolkit/execsnoop OLD_FILES+=usr/share/dtrace/toolkit/hotkernel OLD_FILES+=usr/share/dtrace/toolkit/hotuser OLD_FILES+=usr/share/dtrace/toolkit/opensnoop OLD_FILES+=usr/share/dtrace/toolkit/procsystime OLD_FILES+=usr/share/man/man1/dtrace.1.gz OLD_DIRS+=usr/lib/dtrace OLD_DIRS+=usr/lib32/dtrace OLD_DIRS+=usr/share/dtrace/toolkit OLD_DIRS+=usr/share/dtrace .endif .if ${MK_ZFS} == no OLD_FILES+=boot/gptzfsboot OLD_FILES+=boot/zfsboot OLD_FILES+=boot/zfsloader OLD_FILES+=etc/devd/zfs.conf OLD_FILES+=etc/periodic/daily/404.status-zfs OLD_FILES+=etc/periodic/daily/800.scrub-zfs OLD_FILES+=etc/rc.d/zfs OLD_FILES+=etc/rc.d/zvol OLD_LIBS+=lib/libzfs.so.2 OLD_LIBS+=lib/libzfs_core.so.2 OLD_LIBS+=lib/libzpool.so.2 OLD_FILES+=rescue/zfs OLD_FILES+=rescue/zpool OLD_FILES+=sbin/zfs OLD_FILES+=sbin/zpool OLD_FILES+=usr/bin/zinject OLD_FILES+=usr/bin/ztest OLD_FILES+=usr/lib/libzfs.a OLD_FILES+=usr/lib/libzfs.so OLD_FILES+=usr/lib/libzfs_core.a OLD_FILES+=usr/lib/libzfs_core.so OLD_FILES+=usr/lib/libzfs_core_p.a OLD_FILES+=usr/lib/libzfs_p.a OLD_FILES+=usr/lib/libzpool.a OLD_FILES+=usr/lib/libzpool.so .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libzfs.a OLD_FILES+=usr/lib32/libzfs.so OLD_LIBS+=usr/lib32/libzfs.so.2 OLD_FILES+=usr/lib32/libzfs_core.a OLD_FILES+=usr/lib32/libzfs_core.so OLD_LIBS+=usr/lib32/libzfs_core.so.2 OLD_FILES+=usr/lib32/libzfs_core_p.a OLD_FILES+=usr/lib32/libzfs_p.a OLD_FILES+=usr/lib32/libzpool.a OLD_FILES+=usr/lib32/libzpool.so OLD_LIBS+=usr/lib32/libzpool.so.2 .endif OLD_FILES+=usr/sbin/zdb OLD_FILES+=usr/share/man/man8/zdb.8.gz OLD_FILES+=usr/share/man/man8/zfs.8.gz OLD_FILES+=usr/share/man/man8/zpool.8.gz .endif .if ${MK_CLANG} == no OLD_FILES+=usr/bin/clang OLD_FILES+=usr/bin/clang++ OLD_FILES+=usr/bin/clang-cpp OLD_FILES+=usr/bin/clang-tblgen OLD_FILES+=usr/bin/tblgen OLD_FILES+=usr/include/clang/3.5.1/__wmmintrin_aes.h OLD_FILES+=usr/include/clang/3.5.1/__wmmintrin_pclmul.h OLD_FILES+=usr/include/clang/3.5.1/altivec.h OLD_FILES+=usr/include/clang/3.5.1/ammintrin.h OLD_FILES+=usr/include/clang/3.5.1/arm_acle.h OLD_FILES+=usr/include/clang/3.5.1/arm_neon.h OLD_FILES+=usr/include/clang/3.5.1/avx2intrin.h OLD_FILES+=usr/include/clang/3.5.1/avxintrin.h OLD_FILES+=usr/include/clang/3.5.1/bmi2intrin.h OLD_FILES+=usr/include/clang/3.5.1/bmiintrin.h OLD_FILES+=usr/include/clang/3.5.1/cpuid.h OLD_FILES+=usr/include/clang/3.5.1/emmintrin.h OLD_FILES+=usr/include/clang/3.5.1/f16cintrin.h OLD_FILES+=usr/include/clang/3.5.1/fma4intrin.h OLD_FILES+=usr/include/clang/3.5.1/fmaintrin.h OLD_FILES+=usr/include/clang/3.5.1/ia32intrin.h OLD_FILES+=usr/include/clang/3.5.1/immintrin.h OLD_FILES+=usr/include/clang/3.5.1/lzcntintrin.h OLD_FILES+=usr/include/clang/3.5.1/mm3dnow.h OLD_FILES+=usr/include/clang/3.5.1/mm_malloc.h OLD_FILES+=usr/include/clang/3.5.1/mmintrin.h OLD_FILES+=usr/include/clang/3.5.1/module.modulemap OLD_FILES+=usr/include/clang/3.5.1/nmmintrin.h OLD_FILES+=usr/include/clang/3.5.1/pmmintrin.h OLD_FILES+=usr/include/clang/3.5.1/popcntintrin.h OLD_FILES+=usr/include/clang/3.5.1/prfchwintrin.h OLD_FILES+=usr/include/clang/3.5.1/rdseedintrin.h OLD_FILES+=usr/include/clang/3.5.1/rtmintrin.h OLD_FILES+=usr/include/clang/3.5.1/shaintrin.h OLD_FILES+=usr/include/clang/3.5.1/smmintrin.h OLD_FILES+=usr/include/clang/3.5.1/tbmintrin.h OLD_FILES+=usr/include/clang/3.5.1/tmmintrin.h OLD_FILES+=usr/include/clang/3.5.1/wmmintrin.h OLD_FILES+=usr/include/clang/3.5.1/x86intrin.h OLD_FILES+=usr/include/clang/3.5.1/xmmintrin.h OLD_FILES+=usr/include/clang/3.5.1/xopintrin.h OLD_DIRS+=usr/include/clang/3.5.1 OLD_DIRS+=usr/include/clang OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.asan-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.asan-x86_64.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.asan_cxx-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.asan_cxx-x86_64.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.profile-arm.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.profile-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.profile-x86_64.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.san-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.san-x86_64.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.ubsan-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.ubsan-x86_64.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.ubsan_cxx-i386.a OLD_FILES+=usr/lib/clang/3.5.1/lib/freebsd/libclang_rt.ubsan_cxx-x86_64.a OLD_DIRS+=usr/lib/clang/3.5.1/lib/freebsd OLD_DIRS+=usr/lib/clang/3.5.1/lib OLD_DIRS+=usr/lib/clang/3.5.1 OLD_DIRS+=usr/lib/clang OLD_FILES+=usr/share/doc/llvm/clang/LICENSE.TXT OLD_DIRS+=usr/share/doc/llvm/clang OLD_FILES+=usr/share/doc/llvm/COPYRIGHT.regex OLD_FILES+=usr/share/doc/llvm/LICENSE.TXT OLD_DIRS+=usr/share/doc/llvm OLD_FILES+=usr/share/man/man1/clang.1.gz OLD_FILES+=usr/share/man/man1/clang++.1.gz OLD_FILES+=usr/share/man/man1/clang-cpp.1.gz OLD_FILES+=usr/share/man/man1/tblgen.1.gz .endif .if ${MK_CLANG_EXTRAS} == no OLD_FILES+=usr/bin/bugpoint OLD_FILES+=usr/bin/llc OLD_FILES+=usr/bin/lli OLD_FILES+=usr/bin/llvm-ar OLD_FILES+=usr/bin/llvm-as OLD_FILES+=usr/bin/llvm-bcanalyzer OLD_FILES+=usr/bin/llvm-diff OLD_FILES+=usr/bin/llvm-dis OLD_FILES+=usr/bin/llvm-extract OLD_FILES+=usr/bin/llvm-link OLD_FILES+=usr/bin/llvm-mc OLD_FILES+=usr/bin/llvm-nm OLD_FILES+=usr/bin/llvm-objdump OLD_FILES+=usr/bin/llvm-rtdyld OLD_FILES+=usr/bin/llvm-symbolizer OLD_FILES+=usr/bin/macho-dump OLD_FILES+=usr/bin/opt OLD_FILES+=usr/share/man/man1/bugpoint.1.gz OLD_FILES+=usr/share/man/man1/llc.1.gz OLD_FILES+=usr/share/man/man1/lli.1.gz OLD_FILES+=usr/share/man/man1/llvm-ar.1.gz OLD_FILES+=usr/share/man/man1/llvm-as.1.gz OLD_FILES+=usr/share/man/man1/llvm-bcanalyzer.1.gz OLD_FILES+=usr/share/man/man1/llvm-diff.1.gz OLD_FILES+=usr/share/man/man1/llvm-dis.1.gz OLD_FILES+=usr/share/man/man1/llvm-extract.1.gz OLD_FILES+=usr/share/man/man1/llvm-link.1.gz OLD_FILES+=usr/share/man/man1/llvm-nm.1.gz OLD_FILES+=usr/share/man/man1/llvm-symbolizer.1.gz OLD_FILES+=usr/share/man/man1/opt.1.gz .endif .if ${MK_CPP} == no OLD_FILES+=usr/bin/cpp OLD_FILES+=usr/share/man/man1/cpp.1.gz .endif #.if ${MK_CRYPT} == no # to be filled in #.endif .if ${MK_CTM} == no OLD_FILES+=usr/sbin/ctm OLD_FILES+=usr/sbin/ctm_dequeue OLD_FILES+=usr/sbin/ctm_rmail OLD_FILES+=usr/sbin/ctm_smail OLD_FILES+=usr/share/man/man1/ctm.1.gz OLD_FILES+=usr/share/man/man1/ctm_dequeue.1.gz OLD_FILES+=usr/share/man/man1/ctm_rmail.1.gz OLD_FILES+=usr/share/man/man1/ctm_smail.1.gz OLD_FILES+=usr/share/man/man5/ctm.5.gz .endif .if ${MK_CUSE} == no OLD_FILES+=usr/include/fs/cuse/cuse_defs.h OLD_FILES+=usr/include/fs/cuse/cuse_ioctl.h OLD_FILES+=usr/include/cuse.h OLD_FILES+=usr/lib/libcuse.a OLD_LIBS+=usr/lib/libcuse.so.1 OLD_FILES+=usr/lib/libcuse_p.a OLD_FILES+=usr/share/man/man3/cuse.3.gz OLD_FILES+=usr/share/man/man3/cuse_alloc_unit_number.3.gz OLD_FILES+=usr/share/man/man3/cuse_alloc_unit_number_by_id.3.gz OLD_FILES+=usr/share/man/man3/cuse_copy_in.3.gz OLD_FILES+=usr/share/man/man3/cuse_copy_out.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_create.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_destroy.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_get_current.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_get_per_file_handle.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_get_priv0.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_get_priv1.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_set_per_file_handle.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_set_priv0.3.gz OLD_FILES+=usr/share/man/man3/cuse_dev_set_priv1.3.gz OLD_FILES+=usr/share/man/man3/cuse_free_unit_number.3.gz OLD_FILES+=usr/share/man/man3/cuse_free_unit_number_by_id.3.gz OLD_FILES+=usr/share/man/man3/cuse_get_local.3.gz OLD_FILES+=usr/share/man/man3/cuse_got_peer_signal.3.gz OLD_FILES+=usr/share/man/man3/cuse_init.3.gz OLD_FILES+=usr/share/man/man3/cuse_is_vmalloc_addr.3.gz OLD_FILES+=usr/share/man/man3/cuse_poll_wakeup.3.gz OLD_FILES+=usr/share/man/man3/cuse_set_local.3.gz OLD_FILES+=usr/share/man/man3/cuse_uninit.3.gz OLD_FILES+=usr/share/man/man3/cuse_vmalloc.3.gz OLD_FILES+=usr/share/man/man3/cuse_vmfree.3.gz OLD_FILES+=usr/share/man/man3/cuse_vmoffset.3.gz OLD_FILES+=usr/share/man/man3/cuse_wait_and_process.3.gz OLD_DIRS+=usr/include/fs/cuse .endif # devd(8) not listed here on purpose .if ${MK_CXX} == no OLD_FILES+=usr/bin/CC OLD_FILES+=usr/bin/c++ OLD_FILES+=usr/bin/c++filt OLD_FILES+=usr/bin/g++ OLD_FILES+=usr/libexec/cc1plus .if ${MK_GCC} == no OLD_FILES+=usr/bin/gperf OLD_FILES+=usr/share/info/gperf.info.gz OLD_FILES+=usr/share/man/man1/gperf.1.gz .endif .endif .if ${MK_FILE} == no OLD_FILES+=usr/bin/file OLD_FILES+=usr/include/magic.h OLD_FILES+=usr/lib/libmagic.a OLD_LIBS+=usr/lib/libmagic.so.4 OLD_FILES+=usr/lib/libmagic_p.a OLD_FILES+=usr/share/man/man1/file.1.gz OLD_FILES+=usr/share/man/man3/libmagic.3.gz OLD_FILES+=usr/share/man/man5/magic.5.gz OLD_FILES+=usr/share/misc/magic OLD_FILES+=usr/share/misc/magic.mgc .endif .if ${MK_FINGER} == no OLD_FILES+=usr/bin/finger OLD_FILES+=usr/libexec/fingerd OLD_FILES+=usr/share/man/man1/finger.1.gz OLD_FILES+=usr/share/man/man5/finger.conf.5.gz OLD_FILES+=usr/share/man/man8/fingerd.8.gz .endif .if ${MK_FMTREE} == no OLD_FILES+=usr/sbin/fmtree OLD_FILES+=usr/share/man/man8/fmtree.8.gz .endif .if ${MK_GNUCXX} == no OLD_FILES+=usr/bin/g++ OLD_FILES+=usr/include/c++/4.2/algorithm OLD_FILES+=usr/include/c++/4.2/backward/algo.h OLD_FILES+=usr/include/c++/4.2/backward/algobase.h OLD_FILES+=usr/include/c++/4.2/backward/alloc.h OLD_FILES+=usr/include/c++/4.2/backward/backward_warning.h OLD_FILES+=usr/include/c++/4.2/backward/bvector.h OLD_FILES+=usr/include/c++/4.2/backward/complex.h OLD_FILES+=usr/include/c++/4.2/backward/defalloc.h OLD_FILES+=usr/include/c++/4.2/backward/deque.h OLD_FILES+=usr/include/c++/4.2/backward/fstream.h OLD_FILES+=usr/include/c++/4.2/backward/function.h OLD_FILES+=usr/include/c++/4.2/backward/hash_map.h OLD_FILES+=usr/include/c++/4.2/backward/hash_set.h OLD_FILES+=usr/include/c++/4.2/backward/hashtable.h OLD_FILES+=usr/include/c++/4.2/backward/heap.h OLD_FILES+=usr/include/c++/4.2/backward/iomanip.h OLD_FILES+=usr/include/c++/4.2/backward/iostream.h OLD_FILES+=usr/include/c++/4.2/backward/istream.h OLD_FILES+=usr/include/c++/4.2/backward/iterator.h OLD_FILES+=usr/include/c++/4.2/backward/list.h OLD_FILES+=usr/include/c++/4.2/backward/map.h OLD_FILES+=usr/include/c++/4.2/backward/multimap.h OLD_FILES+=usr/include/c++/4.2/backward/multiset.h OLD_FILES+=usr/include/c++/4.2/backward/new.h OLD_FILES+=usr/include/c++/4.2/backward/ostream.h OLD_FILES+=usr/include/c++/4.2/backward/pair.h OLD_FILES+=usr/include/c++/4.2/backward/queue.h OLD_FILES+=usr/include/c++/4.2/backward/rope.h OLD_FILES+=usr/include/c++/4.2/backward/set.h OLD_FILES+=usr/include/c++/4.2/backward/slist.h OLD_FILES+=usr/include/c++/4.2/backward/stack.h OLD_FILES+=usr/include/c++/4.2/backward/stream.h OLD_FILES+=usr/include/c++/4.2/backward/streambuf.h OLD_FILES+=usr/include/c++/4.2/backward/strstream OLD_FILES+=usr/include/c++/4.2/backward/tempbuf.h OLD_FILES+=usr/include/c++/4.2/backward/tree.h OLD_FILES+=usr/include/c++/4.2/backward/vector.h OLD_FILES+=usr/include/c++/4.2/bits/allocator.h OLD_FILES+=usr/include/c++/4.2/bits/atomic_word.h OLD_FILES+=usr/include/c++/4.2/bits/basic_file.h OLD_FILES+=usr/include/c++/4.2/bits/basic_ios.h OLD_FILES+=usr/include/c++/4.2/bits/basic_ios.tcc OLD_FILES+=usr/include/c++/4.2/bits/basic_string.h OLD_FILES+=usr/include/c++/4.2/bits/basic_string.tcc OLD_FILES+=usr/include/c++/4.2/bits/boost_concept_check.h OLD_FILES+=usr/include/c++/4.2/bits/c++allocator.h OLD_FILES+=usr/include/c++/4.2/bits/c++config.h OLD_FILES+=usr/include/c++/4.2/bits/c++io.h OLD_FILES+=usr/include/c++/4.2/bits/c++locale.h OLD_FILES+=usr/include/c++/4.2/bits/c++locale_internal.h OLD_FILES+=usr/include/c++/4.2/bits/char_traits.h OLD_FILES+=usr/include/c++/4.2/bits/cmath.tcc OLD_FILES+=usr/include/c++/4.2/bits/codecvt.h OLD_FILES+=usr/include/c++/4.2/bits/compatibility.h OLD_FILES+=usr/include/c++/4.2/bits/concept_check.h OLD_FILES+=usr/include/c++/4.2/bits/cpp_type_traits.h OLD_FILES+=usr/include/c++/4.2/bits/cpu_defines.h OLD_FILES+=usr/include/c++/4.2/bits/ctype_base.h OLD_FILES+=usr/include/c++/4.2/bits/ctype_inline.h OLD_FILES+=usr/include/c++/4.2/bits/ctype_noninline.h OLD_FILES+=usr/include/c++/4.2/bits/cxxabi_tweaks.h OLD_FILES+=usr/include/c++/4.2/bits/deque.tcc OLD_FILES+=usr/include/c++/4.2/bits/fstream.tcc OLD_FILES+=usr/include/c++/4.2/bits/functexcept.h OLD_FILES+=usr/include/c++/4.2/bits/gslice.h OLD_FILES+=usr/include/c++/4.2/bits/gslice_array.h OLD_FILES+=usr/include/c++/4.2/bits/gthr-default.h OLD_FILES+=usr/include/c++/4.2/bits/gthr-posix.h OLD_FILES+=usr/include/c++/4.2/bits/gthr-single.h OLD_FILES+=usr/include/c++/4.2/bits/gthr-tpf.h OLD_FILES+=usr/include/c++/4.2/bits/gthr.h OLD_FILES+=usr/include/c++/4.2/bits/indirect_array.h OLD_FILES+=usr/include/c++/4.2/bits/ios_base.h OLD_FILES+=usr/include/c++/4.2/bits/istream.tcc OLD_FILES+=usr/include/c++/4.2/bits/list.tcc OLD_FILES+=usr/include/c++/4.2/bits/locale_classes.h OLD_FILES+=usr/include/c++/4.2/bits/locale_facets.h OLD_FILES+=usr/include/c++/4.2/bits/locale_facets.tcc OLD_FILES+=usr/include/c++/4.2/bits/localefwd.h OLD_FILES+=usr/include/c++/4.2/bits/mask_array.h OLD_FILES+=usr/include/c++/4.2/bits/messages_members.h OLD_FILES+=usr/include/c++/4.2/bits/os_defines.h OLD_FILES+=usr/include/c++/4.2/bits/ostream.tcc OLD_FILES+=usr/include/c++/4.2/bits/ostream_insert.h OLD_FILES+=usr/include/c++/4.2/bits/postypes.h OLD_FILES+=usr/include/c++/4.2/bits/slice_array.h OLD_FILES+=usr/include/c++/4.2/bits/sstream.tcc OLD_FILES+=usr/include/c++/4.2/bits/stl_algo.h OLD_FILES+=usr/include/c++/4.2/bits/stl_algobase.h OLD_FILES+=usr/include/c++/4.2/bits/stl_bvector.h OLD_FILES+=usr/include/c++/4.2/bits/stl_construct.h OLD_FILES+=usr/include/c++/4.2/bits/stl_deque.h OLD_FILES+=usr/include/c++/4.2/bits/stl_function.h OLD_FILES+=usr/include/c++/4.2/bits/stl_heap.h OLD_FILES+=usr/include/c++/4.2/bits/stl_iterator.h OLD_FILES+=usr/include/c++/4.2/bits/stl_iterator_base_funcs.h OLD_FILES+=usr/include/c++/4.2/bits/stl_iterator_base_types.h OLD_FILES+=usr/include/c++/4.2/bits/stl_list.h OLD_FILES+=usr/include/c++/4.2/bits/stl_map.h OLD_FILES+=usr/include/c++/4.2/bits/stl_multimap.h OLD_FILES+=usr/include/c++/4.2/bits/stl_multiset.h OLD_FILES+=usr/include/c++/4.2/bits/stl_numeric.h OLD_FILES+=usr/include/c++/4.2/bits/stl_pair.h OLD_FILES+=usr/include/c++/4.2/bits/stl_queue.h OLD_FILES+=usr/include/c++/4.2/bits/stl_raw_storage_iter.h OLD_FILES+=usr/include/c++/4.2/bits/stl_relops.h OLD_FILES+=usr/include/c++/4.2/bits/stl_set.h OLD_FILES+=usr/include/c++/4.2/bits/stl_stack.h OLD_FILES+=usr/include/c++/4.2/bits/stl_tempbuf.h OLD_FILES+=usr/include/c++/4.2/bits/stl_tree.h OLD_FILES+=usr/include/c++/4.2/bits/stl_uninitialized.h OLD_FILES+=usr/include/c++/4.2/bits/stl_vector.h OLD_FILES+=usr/include/c++/4.2/bits/stream_iterator.h OLD_FILES+=usr/include/c++/4.2/bits/streambuf.tcc OLD_FILES+=usr/include/c++/4.2/bits/streambuf_iterator.h OLD_FILES+=usr/include/c++/4.2/bits/stringfwd.h OLD_FILES+=usr/include/c++/4.2/bits/time_members.h OLD_FILES+=usr/include/c++/4.2/bits/valarray_after.h OLD_FILES+=usr/include/c++/4.2/bits/valarray_array.h OLD_FILES+=usr/include/c++/4.2/bits/valarray_array.tcc OLD_FILES+=usr/include/c++/4.2/bits/valarray_before.h OLD_FILES+=usr/include/c++/4.2/bits/vector.tcc OLD_FILES+=usr/include/c++/4.2/bitset OLD_FILES+=usr/include/c++/4.2/cassert OLD_FILES+=usr/include/c++/4.2/cctype OLD_FILES+=usr/include/c++/4.2/cerrno OLD_FILES+=usr/include/c++/4.2/cfloat OLD_FILES+=usr/include/c++/4.2/ciso646 OLD_FILES+=usr/include/c++/4.2/climits OLD_FILES+=usr/include/c++/4.2/clocale OLD_FILES+=usr/include/c++/4.2/cmath OLD_FILES+=usr/include/c++/4.2/complex OLD_FILES+=usr/include/c++/4.2/csetjmp OLD_FILES+=usr/include/c++/4.2/csignal OLD_FILES+=usr/include/c++/4.2/cstdarg OLD_FILES+=usr/include/c++/4.2/cstddef OLD_FILES+=usr/include/c++/4.2/cstdio OLD_FILES+=usr/include/c++/4.2/cstdlib OLD_FILES+=usr/include/c++/4.2/cstring OLD_FILES+=usr/include/c++/4.2/ctime OLD_FILES+=usr/include/c++/4.2/cwchar OLD_FILES+=usr/include/c++/4.2/cwctype OLD_FILES+=usr/include/c++/4.2/cxxabi.h OLD_FILES+=usr/include/c++/4.2/debug/bitset OLD_FILES+=usr/include/c++/4.2/debug/debug.h OLD_FILES+=usr/include/c++/4.2/debug/deque OLD_FILES+=usr/include/c++/4.2/debug/formatter.h OLD_FILES+=usr/include/c++/4.2/debug/functions.h OLD_FILES+=usr/include/c++/4.2/debug/hash_map OLD_FILES+=usr/include/c++/4.2/debug/hash_map.h OLD_FILES+=usr/include/c++/4.2/debug/hash_multimap.h OLD_FILES+=usr/include/c++/4.2/debug/hash_multiset.h OLD_FILES+=usr/include/c++/4.2/debug/hash_set OLD_FILES+=usr/include/c++/4.2/debug/hash_set.h OLD_FILES+=usr/include/c++/4.2/debug/list OLD_FILES+=usr/include/c++/4.2/debug/macros.h OLD_FILES+=usr/include/c++/4.2/debug/map OLD_FILES+=usr/include/c++/4.2/debug/map.h OLD_FILES+=usr/include/c++/4.2/debug/multimap.h OLD_FILES+=usr/include/c++/4.2/debug/multiset.h OLD_FILES+=usr/include/c++/4.2/debug/safe_base.h OLD_FILES+=usr/include/c++/4.2/debug/safe_iterator.h OLD_FILES+=usr/include/c++/4.2/debug/safe_iterator.tcc OLD_FILES+=usr/include/c++/4.2/debug/safe_sequence.h OLD_FILES+=usr/include/c++/4.2/debug/set OLD_FILES+=usr/include/c++/4.2/debug/set.h OLD_FILES+=usr/include/c++/4.2/debug/string OLD_FILES+=usr/include/c++/4.2/debug/vector OLD_FILES+=usr/include/c++/4.2/deque OLD_FILES+=usr/include/c++/4.2/exception OLD_FILES+=usr/include/c++/4.2/exception_defines.h OLD_FILES+=usr/include/c++/4.2/ext/algorithm OLD_FILES+=usr/include/c++/4.2/ext/array_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/atomicity.h OLD_FILES+=usr/include/c++/4.2/ext/bitmap_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/codecvt_specializations.h OLD_FILES+=usr/include/c++/4.2/ext/concurrence.h OLD_FILES+=usr/include/c++/4.2/ext/debug_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/functional OLD_FILES+=usr/include/c++/4.2/ext/hash_fun.h OLD_FILES+=usr/include/c++/4.2/ext/hash_map OLD_FILES+=usr/include/c++/4.2/ext/hash_set OLD_FILES+=usr/include/c++/4.2/ext/hashtable.h OLD_FILES+=usr/include/c++/4.2/ext/iterator OLD_FILES+=usr/include/c++/4.2/ext/malloc_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/memory OLD_FILES+=usr/include/c++/4.2/ext/mt_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/new_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/numeric OLD_FILES+=usr/include/c++/4.2/ext/numeric_traits.h OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/assoc_container.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/basic_tree_policy/basic_tree_policy_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/basic_tree_policy/null_node_metadata.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/basic_tree_policy/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/basic_types.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/bin_search_tree_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/cond_dtor_entry_dealtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/cond_key_dtor_entry_dealtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/node_iterators.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/point_iterators.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/r_erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/rotate_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/bin_search_tree_/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/binary_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/const_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/const_point_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/entry_cmp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/entry_pred.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/resize_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binary_heap_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_/binomial_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/binomial_heap_base_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/binomial_heap_base_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/cc_ht_map_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/cmp_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/cond_key_dtor_entry_dealtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/constructor_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/constructor_destructor_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/constructor_destructor_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/debug_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/debug_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/entry_list_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/erase_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/erase_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/find_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/insert_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/insert_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/resize_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/resize_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/resize_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/size_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/standard_policies.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cc_hash_table_map_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/cond_dealtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/container_base_dispatch.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/eq_fn/eq_by_less.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/eq_fn/hash_eq_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/constructor_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/constructor_destructor_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/constructor_destructor_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/debug_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/debug_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/erase_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/erase_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/find_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/find_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/gp_ht_map_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/insert_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/insert_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/iterator_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/resize_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/resize_no_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/resize_store_hash_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/standard_policies.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/gp_hash_table_map_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/direct_mask_range_hashing_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/direct_mod_range_hashing_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/linear_probe_fn_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/mask_based_range_hashing.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/mod_based_range_hashing.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/probe_fn_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/quadratic_probe_fn_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/ranged_hash_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/ranged_probe_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/sample_probe_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/sample_range_hashing.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/sample_ranged_hash_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/hash_fn/sample_ranged_probe_fn.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/const_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/const_point_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/left_child_next_sibling_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/node.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/null_metadata.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/left_child_next_sibling_heap_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/constructor_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/entry_metadata_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/lu_map_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_map_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_policy/counter_lu_metadata.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_policy/counter_lu_policy_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_policy/mtf_lu_policy_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/list_update_policy/sample_update_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/map_debug_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/cond_dtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/node_iterators.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/ov_tree_map_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/ov_tree_map_/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/pairing_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pairing_heap_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/child_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/cond_dtor_entry_dealtor.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/const_child_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/head.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/insert_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/internal_node.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/iterators_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/leaf.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/node_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/node_iterators.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/node_metadata_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/pat_trie_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/point_iterators.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/policy_access_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/r_erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/rotate_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/split_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/split_join_branch_bag.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/synth_e_access_traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/pat_trie_/update_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/priority_queue_base_dispatch.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/node.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/rb_tree_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rb_tree_map_/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/rc.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/rc_binomial_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/rc_binomial_heap_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/cc_hash_max_collision_check_resize_trigger_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/hash_exponential_size_policy_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/hash_load_check_resize_trigger_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/hash_load_check_resize_trigger_size_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/hash_prime_size_policy_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/hash_standard_resize_policy_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/sample_resize_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/sample_resize_trigger.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/resize_policy/sample_size_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/info_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/node.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/splay_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/splay_tree_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/splay_tree_/traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/standard_policies.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/constructors_destructor_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/debug_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/erase_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/find_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/insert_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/split_join_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/thin_heap_.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/thin_heap_/trace_fn_imps.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/tree_policy/node_metadata_selector.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/tree_policy/null_node_update_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/tree_policy/order_statistics_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/tree_policy/sample_tree_node_update.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/tree_trace_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/node_metadata_selector.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/null_node_update_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/order_statistics_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/prefix_search_node_update_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/sample_trie_e_access_traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/sample_trie_node_update.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/string_trie_e_access_traits_imp.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/trie_policy/trie_policy_base.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/type_utils.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/types_traits.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/unordered_iterator/const_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/unordered_iterator/const_point_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/unordered_iterator/iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/detail/unordered_iterator/point_iterator.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/exception.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/hash_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/list_update_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/priority_queue.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/tag_and_trait.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/tree_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pb_ds/trie_policy.hpp OLD_FILES+=usr/include/c++/4.2/ext/pod_char_traits.h OLD_FILES+=usr/include/c++/4.2/ext/pool_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/rb_tree OLD_FILES+=usr/include/c++/4.2/ext/rc_string_base.h OLD_FILES+=usr/include/c++/4.2/ext/rope OLD_FILES+=usr/include/c++/4.2/ext/ropeimpl.h OLD_FILES+=usr/include/c++/4.2/ext/slist OLD_FILES+=usr/include/c++/4.2/ext/sso_string_base.h OLD_FILES+=usr/include/c++/4.2/ext/stdio_filebuf.h OLD_FILES+=usr/include/c++/4.2/ext/stdio_sync_filebuf.h OLD_FILES+=usr/include/c++/4.2/ext/throw_allocator.h OLD_FILES+=usr/include/c++/4.2/ext/type_traits.h OLD_FILES+=usr/include/c++/4.2/ext/typelist.h OLD_FILES+=usr/include/c++/4.2/ext/vstring.h OLD_FILES+=usr/include/c++/4.2/ext/vstring.tcc OLD_FILES+=usr/include/c++/4.2/ext/vstring_fwd.h OLD_FILES+=usr/include/c++/4.2/ext/vstring_util.h OLD_FILES+=usr/include/c++/4.2/fstream OLD_FILES+=usr/include/c++/4.2/functional OLD_FILES+=usr/include/c++/4.2/iomanip OLD_FILES+=usr/include/c++/4.2/ios OLD_FILES+=usr/include/c++/4.2/iosfwd OLD_FILES+=usr/include/c++/4.2/iostream OLD_FILES+=usr/include/c++/4.2/istream OLD_FILES+=usr/include/c++/4.2/iterator OLD_FILES+=usr/include/c++/4.2/limits OLD_FILES+=usr/include/c++/4.2/list OLD_FILES+=usr/include/c++/4.2/locale OLD_FILES+=usr/include/c++/4.2/map OLD_FILES+=usr/include/c++/4.2/memory OLD_FILES+=usr/include/c++/4.2/new OLD_FILES+=usr/include/c++/4.2/numeric OLD_FILES+=usr/include/c++/4.2/ostream OLD_FILES+=usr/include/c++/4.2/queue OLD_FILES+=usr/include/c++/4.2/set OLD_FILES+=usr/include/c++/4.2/sstream OLD_FILES+=usr/include/c++/4.2/stack OLD_FILES+=usr/include/c++/4.2/stdexcept OLD_FILES+=usr/include/c++/4.2/streambuf OLD_FILES+=usr/include/c++/4.2/string OLD_FILES+=usr/include/c++/4.2/tr1/array OLD_FILES+=usr/include/c++/4.2/tr1/bind_iterate.h OLD_FILES+=usr/include/c++/4.2/tr1/bind_repeat.h OLD_FILES+=usr/include/c++/4.2/tr1/boost_shared_ptr.h OLD_FILES+=usr/include/c++/4.2/tr1/cctype OLD_FILES+=usr/include/c++/4.2/tr1/cfenv OLD_FILES+=usr/include/c++/4.2/tr1/cfloat OLD_FILES+=usr/include/c++/4.2/tr1/cinttypes OLD_FILES+=usr/include/c++/4.2/tr1/climits OLD_FILES+=usr/include/c++/4.2/tr1/cmath OLD_FILES+=usr/include/c++/4.2/tr1/common.h OLD_FILES+=usr/include/c++/4.2/tr1/complex OLD_FILES+=usr/include/c++/4.2/tr1/cstdarg OLD_FILES+=usr/include/c++/4.2/tr1/cstdbool OLD_FILES+=usr/include/c++/4.2/tr1/cstdint OLD_FILES+=usr/include/c++/4.2/tr1/cstdio OLD_FILES+=usr/include/c++/4.2/tr1/cstdlib OLD_FILES+=usr/include/c++/4.2/tr1/ctgmath OLD_FILES+=usr/include/c++/4.2/tr1/ctime OLD_FILES+=usr/include/c++/4.2/tr1/ctype.h OLD_FILES+=usr/include/c++/4.2/tr1/cwchar OLD_FILES+=usr/include/c++/4.2/tr1/cwctype OLD_FILES+=usr/include/c++/4.2/tr1/fenv.h OLD_FILES+=usr/include/c++/4.2/tr1/float.h OLD_FILES+=usr/include/c++/4.2/tr1/functional OLD_FILES+=usr/include/c++/4.2/tr1/functional_hash.h OLD_FILES+=usr/include/c++/4.2/tr1/functional_iterate.h OLD_FILES+=usr/include/c++/4.2/tr1/hashtable OLD_FILES+=usr/include/c++/4.2/tr1/hashtable_policy.h OLD_FILES+=usr/include/c++/4.2/tr1/inttypes.h OLD_FILES+=usr/include/c++/4.2/tr1/limits.h OLD_FILES+=usr/include/c++/4.2/tr1/math.h OLD_FILES+=usr/include/c++/4.2/tr1/memory OLD_FILES+=usr/include/c++/4.2/tr1/mu_iterate.h OLD_FILES+=usr/include/c++/4.2/tr1/random OLD_FILES+=usr/include/c++/4.2/tr1/random.tcc OLD_FILES+=usr/include/c++/4.2/tr1/ref_fwd.h OLD_FILES+=usr/include/c++/4.2/tr1/ref_wrap_iterate.h OLD_FILES+=usr/include/c++/4.2/tr1/repeat.h OLD_FILES+=usr/include/c++/4.2/tr1/stdarg.h OLD_FILES+=usr/include/c++/4.2/tr1/stdbool.h OLD_FILES+=usr/include/c++/4.2/tr1/stdint.h OLD_FILES+=usr/include/c++/4.2/tr1/stdio.h OLD_FILES+=usr/include/c++/4.2/tr1/stdlib.h OLD_FILES+=usr/include/c++/4.2/tr1/tgmath.h OLD_FILES+=usr/include/c++/4.2/tr1/tuple OLD_FILES+=usr/include/c++/4.2/tr1/tuple_defs.h OLD_FILES+=usr/include/c++/4.2/tr1/tuple_iterate.h OLD_FILES+=usr/include/c++/4.2/tr1/type_traits OLD_FILES+=usr/include/c++/4.2/tr1/type_traits_fwd.h OLD_FILES+=usr/include/c++/4.2/tr1/unordered_map OLD_FILES+=usr/include/c++/4.2/tr1/unordered_set OLD_FILES+=usr/include/c++/4.2/tr1/utility OLD_FILES+=usr/include/c++/4.2/tr1/wchar.h OLD_FILES+=usr/include/c++/4.2/tr1/wctype.h OLD_FILES+=usr/include/c++/4.2/typeinfo OLD_FILES+=usr/include/c++/4.2/utility OLD_FILES+=usr/include/c++/4.2/valarray OLD_FILES+=usr/include/c++/4.2/vector OLD_FILES+=usr/lib/libstdc++.a OLD_FILES+=usr/lib/libstdc++.so OLD_LIBS+=usr/lib/libstdc++.so.6 OLD_FILES+=usr/lib/libstdc++_p.a OLD_FILES+=usr/lib/libsupc++.a OLD_FILES+=usr/lib/libsupc++.so OLD_LIBS+=usr/lib/libsupc++.so.1 OLD_FILES+=usr/lib/libsupc++_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libstdc++.a OLD_FILES+=usr/lib32/libstdc++.so OLD_LIBS+=usr/lib32/libstdc++.so.6 OLD_FILES+=usr/lib32/libstdc++_p.a OLD_FILES+=usr/lib32/libsupc++.a OLD_FILES+=usr/lib32/libsupc++.so OLD_LIBS+=usr/lib32/libsupc++.so.1 OLD_FILES+=usr/lib32/libsupc++_p.a .endif OLD_FILES+=usr/libexec/cc1plus .endif .if ${MK_DICT} == no OLD_FILES+=usr/share/dict/README OLD_FILES+=usr/share/dict/eign OLD_FILES+=usr/share/dict/freebsd OLD_FILES+=usr/share/dict/propernames OLD_FILES+=usr/share/dict/web2 OLD_FILES+=usr/share/dict/web2a OLD_FILES+=usr/share/dict/words OLD_DIRS+=usr/share/dict .endif .if ${MK_DMAGENT} == no OLD_FILES+=etc/dma/dma.conf OLD_FILES+=usr/libexec/dma OLD_FILES+=usr/libexec/dma-mbox-create OLD_FILES+=usr/share/man/man8/dma.8.gz OLD_FILES+=usr/share/examples/dma/mailer.conf .endif .if ${MK_EE} == no OLD_FILES+=usr/bin/edit OLD_FILES+=usr/bin/ee OLD_FILES+=usr/bin/ree OLD_FILES+=usr/share/man/man1/edit.1.gz OLD_FILES+=usr/share/man/man1/ee.1.gz OLD_FILES+=usr/share/man/man1/ree.1.gz OLD_FILES+=usr/share/nls/C/ee.cat OLD_FILES+=usr/share/nls/de_DE.ISO8859-1/ee.cat OLD_FILES+=usr/share/nls/fr_FR.ISO8859-1/ee.cat OLD_FILES+=usr/share/nls/hu_HU.ISO8859-2/ee.cat OLD_FILES+=usr/share/nls/pl_PL.ISO8859-2/ee.cat OLD_FILES+=usr/share/nls/pt_BR.ISO8859-1/ee.cat OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/ee.cat OLD_FILES+=usr/share/nls/uk_UA.KOI8-U/ee.cat .endif .if ${MK_ELFTOOLCHAIN_TOOLS} == no OLD_FILES+=usr/bin/elfcopy OLD_FILES+=usr/share/man/man1/elfcopy.1.gz .endif #.if ${MK_EXAMPLES} == no # to be filled in #.endif .if ${MK_FLOPPY} == no OLD_FILES+=usr/sbin/fdcontrol OLD_FILES+=usr/sbin/fdformat OLD_FILES+=usr/sbin/fdread OLD_FILES+=usr/sbin/fdwrite OLD_FILES+=usr/share/man/man1/fdformat.1.gz OLD_FILES+=usr/share/man/man1/fdread.1.gz OLD_FILES+=usr/share/man/man1/fdwrite.1.gz OLD_FILES+=usr/share/man/man8/fdcontrol.8.gz .endif .if ${MK_FREEBSD_UPDATE} == no OLD_FILES+=etc/freebsd-update.conf OLD_FILES+=usr/sbin/freebsd-update OLD_FILES+=usr/share/examples/etc/freebsd-update.conf OLD_FILES+=usr/share/man/man5/freebsd-update.conf.5.gz OLD_FILES+=usr/share/man/man8/freebsd-update.8.gz .endif .if ${MK_FTP} == no OLD_FILES+=etc/pam.d/ftp OLD_FILES+=etc/pam.d/ftpd OLD_FILES+=usr/bin/ftp OLD_FILES+=usr/bin/gate-ftp OLD_FILES+=usr/bin/pftp OLD_LIBS+=usr/lib/pam_ftpusers.so.5 OLD_FILES+=usr/libexec/ftpd OLD_FILES+=usr/share/man/man1/ftp.1.gz OLD_FILES+=usr/share/man/man1/gate-ftp.1.gz OLD_FILES+=usr/share/man/man1/pftp.1.gz OLD_FILES+=usr/share/man/man5/ftpchroot.5.gz OLD_FILES+=usr/share/man/man8/ftpd.8.gz OLD_FILES+=usr/share/man/man8/pam_ftpusers.8.gz .endif .if ${MK_GAMES} == no OLD_FILES+=usr/games/bcd OLD_FILES+=usr/games/caesar OLD_FILES+=usr/games/factor OLD_FILES+=usr/games/fortune OLD_FILES+=usr/games/grdc OLD_FILES+=usr/games/morse OLD_FILES+=usr/games/number OLD_FILES+=usr/games/pom OLD_FILES+=usr/games/ppt OLD_FILES+=usr/games/primes OLD_FILES+=usr/games/random OLD_FILES+=usr/games/rot13 OLD_FILES+=usr/games/strfile OLD_FILES+=usr/games/unstr OLD_DIRS+=usr/games OLD_FILES+=usr/share/games/fortune/fortunes OLD_FILES+=usr/share/games/fortune/fortunes.dat OLD_FILES+=usr/share/games/fortune/freebsd-tips OLD_FILES+=usr/share/games/fortune/freebsd-tips.dat OLD_FILES+=usr/share/games/fortune/gerrold.limerick OLD_FILES+=usr/share/games/fortune/gerrold.limerick.dat OLD_FILES+=usr/share/games/fortune/limerick OLD_FILES+=usr/share/games/fortune/limerick.dat OLD_FILES+=usr/share/games/fortune/murphy OLD_FILES+=usr/share/games/fortune/murphy-o OLD_FILES+=usr/share/games/fortune/murphy-o.dat OLD_FILES+=usr/share/games/fortune/murphy.dat OLD_FILES+=usr/share/games/fortune/startrek OLD_FILES+=usr/share/games/fortune/startrek.dat OLD_FILES+=usr/share/games/fortune/zippy OLD_FILES+=usr/share/games/fortune/zippy.dat OLD_DIRS+=usr/share/games/fortune OLD_DIRS+=usr/share/games OLD_FILES+=usr/share/man/man6/bcd.6.gz OLD_FILES+=usr/share/man/man6/caesar.6.gz OLD_FILES+=usr/share/man/man6/factor.6.gz OLD_FILES+=usr/share/man/man6/fortune.6.gz OLD_FILES+=usr/share/man/man6/grdc.6.gz OLD_FILES+=usr/share/man/man6/morse.6.gz OLD_FILES+=usr/share/man/man6/number.6.gz OLD_FILES+=usr/share/man/man6/pom.6.gz OLD_FILES+=usr/share/man/man6/ppt.6.gz OLD_FILES+=usr/share/man/man6/primes.6.gz OLD_FILES+=usr/share/man/man6/random.6.gz OLD_FILES+=usr/share/man/man6/rot13.6.gz OLD_FILES+=usr/share/man/man8/strfile.8.gz OLD_FILES+=usr/share/man/man8/unstr.8.gz .endif .if ${MK_GCC} == no OLD_FILES+=usr/bin/c++filt OLD_FILES+=usr/bin/g++ OLD_FILES+=usr/bin/gcc OLD_FILES+=usr/bin/gcov OLD_FILES+=usr/bin/gcpp .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" OLD_FILES+=usr/include/gcc/4.2/__wmmintrin_aes.h OLD_FILES+=usr/include/gcc/4.2/__wmmintrin_pclmul.h OLD_FILES+=usr/include/gcc/4.2/ammintrin.h OLD_FILES+=usr/include/gcc/4.2/emmintrin.h OLD_FILES+=usr/include/gcc/4.2/mm3dnow.h OLD_FILES+=usr/include/gcc/4.2/mm_malloc.h OLD_FILES+=usr/include/gcc/4.2/mmintrin.h OLD_FILES+=usr/include/gcc/4.2/pmmintrin.h OLD_FILES+=usr/include/gcc/4.2/tmmintrin.h OLD_FILES+=usr/include/gcc/4.2/wmmintrin.h OLD_FILES+=usr/include/gcc/4.2/xmmintrin.h .elif ${TARGET_ARCH} == "arm" OLD_FILES+=usr/include/gcc/4.2/mmintrin.h .elif ${TARGET_ARCH} == "powerpc" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/include/gcc/4.2/altivec.h OLD_FILES+=usr/include/gcc/4.2/ppc-asm.h OLD_FILES+=usr/include/gcc/4.2/spe.h .endif OLD_FILES+=usr/libexec/cc1 OLD_FILES+=usr/libexec/cc1plus OLD_FILES+=usr/share/info/cpp.info.gz OLD_FILES+=usr/share/info/cppinternals.info.gz OLD_FILES+=usr/share/info/gcc.info.gz OLD_FILES+=usr/share/info/gccint.info.gz OLD_FILES+=usr/share/man/man1/g++.1.gz OLD_FILES+=usr/share/man/man1/gcc.1.gz OLD_FILES+=usr/share/man/man1/gcov.1.gz OLD_FILES+=usr/share/man/man1/gcpp.1.gz .endif .if ${MK_GCOV} == no OLD_FILES+=usr/bin/gcov OLD_FILES+=usr/share/man/man1/gcov.1.gz .endif .if ${MK_GDB} == no OLD_FILES+=usr/bin/gdb OLD_FILES+=usr/bin/gdbserver OLD_FILES+=usr/bin/gdbtui OLD_FILES+=usr/bin/kgdb OLD_FILES+=usr/share/info/gdb.info.gz OLD_FILES+=usr/share/info/gdbint.info.gz OLD_FILES+=usr/share/info/stabs.info.gz OLD_FILES+=usr/share/man/man1/gdb.1.gz OLD_FILES+=usr/share/man/man1/gdbserver.1.gz OLD_FILES+=usr/share/man/man1/kgdb.1.gz .endif .if ${MK_GPIO} == no OLD_FILES+=usr/include/libgpio.h OLD_FILES+=usr/lib/libgpio.a OLD_FILES+=usr/lib/libgpio.so OLD_LIBS+=usr/lib/libgpio.so.0 OLD_FILES+=usr/lib/libgpio_p.a OLD_FILES+=usr/lib32/libgpio.a OLD_FILES+=usr/lib32/libgpio.so OLD_LIBS+=usr/lib32/libgpio.so.0 OLD_FILES+=usr/lib32/libgpio_p.a OLD_FILES+=usr/sbin/gpioctl OLD_FILES+=usr/share/man/man3/gpio.3.gz OLD_FILES+=usr/share/man/man3/gpio_close.3.gz OLD_FILES+=usr/share/man/man3/gpio_open.3.gz OLD_FILES+=usr/share/man/man3/gpio_open_device.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_config.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_get.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_high.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_input.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_invin.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_invout.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_list.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_low.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_opendrain.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_output.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_pulldown.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_pullup.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_pulsate.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_pushpull.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_set.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_set_flags.3.gz OLD_FILES+=usr/share/man/man3/gpio_pin_tristate.3.gz OLD_FILES+=usr/share/man/man8/gpioctl.8.gz .endif # Also includes vgrind(1) .if ${MK_GROFF} == no OLD_FILES+=usr/bin/addftinfo OLD_FILES+=usr/bin/afmtodit OLD_FILES+=usr/bin/eqn OLD_FILES+=usr/bin/grn OLD_FILES+=usr/bin/grodvi OLD_FILES+=usr/bin/groff OLD_FILES+=usr/bin/grog OLD_FILES+=usr/bin/grolbp OLD_FILES+=usr/bin/grolj4 OLD_FILES+=usr/bin/grops OLD_FILES+=usr/bin/grotty OLD_FILES+=usr/bin/hpftodit OLD_FILES+=usr/bin/indxbib OLD_FILES+=usr/bin/lkbib OLD_FILES+=usr/bin/lookbib OLD_FILES+=usr/bin/mmroff OLD_FILES+=usr/bin/neqn OLD_FILES+=usr/bin/nroff OLD_FILES+=usr/bin/pfbtops OLD_FILES+=usr/bin/pic OLD_FILES+=usr/bin/post-grohtml OLD_FILES+=usr/bin/pre-grohtml OLD_FILES+=usr/bin/psroff OLD_FILES+=usr/bin/refer OLD_FILES+=usr/bin/soelim OLD_FILES+=usr/bin/tbl OLD_FILES+=usr/bin/tfmtodit OLD_FILES+=usr/bin/troff OLD_FILES+=usr/bin/vgrind OLD_FILES+=usr/libexec/vfontedpr +OLD_FILES+=usr/share/dict/eign +OLD_FILES+=usr/share/doc/papers/beyond43.ascii.gz +OLD_FILES+=usr/share/doc/papers/bio.ascii.gz +OLD_FILES+=usr/share/doc/papers/contents.ascii.gz +OLD_FILES+=usr/share/doc/papers/devfs.ascii.gz +OLD_FILES+=usr/share/doc/papers/diskperf.ascii.gz +OLD_FILES+=usr/share/doc/papers/fsinterface.ascii.gz +OLD_FILES+=usr/share/doc/papers/hwpmc.ascii.gz +OLD_FILES+=usr/share/doc/papers/jail.ascii.gz +OLD_FILES+=usr/share/doc/papers/kernmalloc.ascii.gz +OLD_FILES+=usr/share/doc/papers/kerntune.ascii.gz +OLD_FILES+=usr/share/doc/papers/malloc.ascii.gz +OLD_FILES+=usr/share/doc/papers/newvm.ascii.gz +OLD_FILES+=usr/share/doc/papers/releng.ascii.gz +OLD_FILES+=usr/share/doc/papers/sysperf.ascii.gz +OLD_FILES+=usr/share/doc/papers/timecounter.ascii.gz +OLD_FILES+=usr/share/doc/psd/01.cacm/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/02.implement/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/03.iosys/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/04.uprog/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/05.sysman/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/06.Clang/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/12.make/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/13.rcs/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/13.rcs/rcs_func.ascii.gz +OLD_FILES+=usr/share/doc/psd/15.yacc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/16.lex/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/17.m4/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/18.gprof/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/20.ipctut/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/21.ipc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/22.rpcgen/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/23.rpc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/24.xdr/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/25.xdrrfc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/26.rpcrfc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/27.nfsrfc/paper.ascii.gz +OLD_FILES+=usr/share/doc/psd/Title.ascii.gz +OLD_FILES+=usr/share/doc/psd/contents.ascii.gz +OLD_FILES+=usr/share/doc/smm/01.setup/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/02.config/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/03.fsck/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/04.quotas/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/05.fastfs/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/06.nfs/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/07.lpd/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/08.sendmailop/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/11.timedop/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/12.timed/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/18.net/paper.ascii.gz +OLD_FILES+=usr/share/doc/smm/Title.ascii.gz +OLD_FILES+=usr/share/doc/smm/contents.ascii.gz +OLD_FILES+=usr/share/doc/usd/04.csh/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/05.dc/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/06.bc/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/07.mail/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/10.exref/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/10.exref/summary.ascii.gz +OLD_FILES+=usr/share/doc/usd/11.edit/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/12.vi/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/12.vi/summary.ascii.gz +OLD_FILES+=usr/share/doc/usd/12.vi/viapwh.ascii.gz +OLD_FILES+=usr/share/doc/usd/13.viref/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/18.msdiffs/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/19.memacros/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/20.meref/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/21.troff/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/22.trofftut/paper.ascii.gz +OLD_FILES+=usr/share/doc/usd/Title.ascii.gz +OLD_FILES+=usr/share/doc/usd/contents.ascii.gz OLD_FILES+=usr/share/groff_font/devX100-12/CB OLD_FILES+=usr/share/groff_font/devX100-12/CBI OLD_FILES+=usr/share/groff_font/devX100-12/CI OLD_FILES+=usr/share/groff_font/devX100-12/CR OLD_FILES+=usr/share/groff_font/devX100-12/DESC OLD_FILES+=usr/share/groff_font/devX100-12/HB OLD_FILES+=usr/share/groff_font/devX100-12/HBI OLD_FILES+=usr/share/groff_font/devX100-12/HI OLD_FILES+=usr/share/groff_font/devX100-12/HR OLD_FILES+=usr/share/groff_font/devX100-12/NB OLD_FILES+=usr/share/groff_font/devX100-12/NBI OLD_FILES+=usr/share/groff_font/devX100-12/NI OLD_FILES+=usr/share/groff_font/devX100-12/NR OLD_FILES+=usr/share/groff_font/devX100-12/S OLD_FILES+=usr/share/groff_font/devX100-12/TB OLD_FILES+=usr/share/groff_font/devX100-12/TBI OLD_FILES+=usr/share/groff_font/devX100-12/TI OLD_FILES+=usr/share/groff_font/devX100-12/TR OLD_DIRS+=usr/share/groff_font/devX100-12 OLD_FILES+=usr/share/groff_font/devX100/CB OLD_FILES+=usr/share/groff_font/devX100/CBI OLD_FILES+=usr/share/groff_font/devX100/CI OLD_FILES+=usr/share/groff_font/devX100/CR OLD_FILES+=usr/share/groff_font/devX100/DESC OLD_FILES+=usr/share/groff_font/devX100/HB OLD_FILES+=usr/share/groff_font/devX100/HBI OLD_FILES+=usr/share/groff_font/devX100/HI OLD_FILES+=usr/share/groff_font/devX100/HR OLD_FILES+=usr/share/groff_font/devX100/NB OLD_FILES+=usr/share/groff_font/devX100/NBI OLD_FILES+=usr/share/groff_font/devX100/NI OLD_FILES+=usr/share/groff_font/devX100/NR OLD_FILES+=usr/share/groff_font/devX100/S OLD_FILES+=usr/share/groff_font/devX100/TB OLD_FILES+=usr/share/groff_font/devX100/TBI OLD_FILES+=usr/share/groff_font/devX100/TI OLD_FILES+=usr/share/groff_font/devX100/TR OLD_DIRS+=usr/share/groff_font/devX100 OLD_FILES+=usr/share/groff_font/devX75-12/CB OLD_FILES+=usr/share/groff_font/devX75-12/CBI OLD_FILES+=usr/share/groff_font/devX75-12/CI OLD_FILES+=usr/share/groff_font/devX75-12/CR OLD_FILES+=usr/share/groff_font/devX75-12/DESC OLD_FILES+=usr/share/groff_font/devX75-12/HB OLD_FILES+=usr/share/groff_font/devX75-12/HBI OLD_FILES+=usr/share/groff_font/devX75-12/HI OLD_FILES+=usr/share/groff_font/devX75-12/HR OLD_FILES+=usr/share/groff_font/devX75-12/NB OLD_FILES+=usr/share/groff_font/devX75-12/NBI OLD_FILES+=usr/share/groff_font/devX75-12/NI OLD_FILES+=usr/share/groff_font/devX75-12/NR OLD_FILES+=usr/share/groff_font/devX75-12/S OLD_FILES+=usr/share/groff_font/devX75-12/TB OLD_FILES+=usr/share/groff_font/devX75-12/TBI OLD_FILES+=usr/share/groff_font/devX75-12/TI OLD_FILES+=usr/share/groff_font/devX75-12/TR OLD_DIRS+=usr/share/groff_font/devX75-12 OLD_FILES+=usr/share/groff_font/devX75/CB OLD_FILES+=usr/share/groff_font/devX75/CBI OLD_FILES+=usr/share/groff_font/devX75/CI OLD_FILES+=usr/share/groff_font/devX75/CR OLD_FILES+=usr/share/groff_font/devX75/DESC OLD_FILES+=usr/share/groff_font/devX75/HB OLD_FILES+=usr/share/groff_font/devX75/HBI OLD_FILES+=usr/share/groff_font/devX75/HI OLD_FILES+=usr/share/groff_font/devX75/HR OLD_FILES+=usr/share/groff_font/devX75/NB OLD_FILES+=usr/share/groff_font/devX75/NBI OLD_FILES+=usr/share/groff_font/devX75/NI OLD_FILES+=usr/share/groff_font/devX75/NR OLD_FILES+=usr/share/groff_font/devX75/S OLD_FILES+=usr/share/groff_font/devX75/TB OLD_FILES+=usr/share/groff_font/devX75/TBI OLD_FILES+=usr/share/groff_font/devX75/TI OLD_FILES+=usr/share/groff_font/devX75/TR OLD_DIRS+=usr/share/groff_font/devX75 OLD_FILES+=usr/share/groff_font/devascii/B OLD_FILES+=usr/share/groff_font/devascii/BI OLD_FILES+=usr/share/groff_font/devascii/CW OLD_FILES+=usr/share/groff_font/devascii/DESC OLD_FILES+=usr/share/groff_font/devascii/I OLD_FILES+=usr/share/groff_font/devascii/L OLD_FILES+=usr/share/groff_font/devascii/R OLD_FILES+=usr/share/groff_font/devascii/S OLD_DIRS+=usr/share/groff_font/devascii OLD_FILES+=usr/share/groff_font/devcp1047/B OLD_FILES+=usr/share/groff_font/devcp1047/BI OLD_FILES+=usr/share/groff_font/devcp1047/CW OLD_FILES+=usr/share/groff_font/devcp1047/DESC OLD_FILES+=usr/share/groff_font/devcp1047/I OLD_FILES+=usr/share/groff_font/devcp1047/L OLD_FILES+=usr/share/groff_font/devcp1047/R OLD_FILES+=usr/share/groff_font/devcp1047/S OLD_DIRS+=usr/share/groff_font/devcp1047 OLD_FILES+=usr/share/groff_font/devdvi/CW OLD_FILES+=usr/share/groff_font/devdvi/CWEC OLD_FILES+=usr/share/groff_font/devdvi/CWI OLD_FILES+=usr/share/groff_font/devdvi/CWIEC OLD_FILES+=usr/share/groff_font/devdvi/CWITC OLD_FILES+=usr/share/groff_font/devdvi/CWTC OLD_FILES+=usr/share/groff_font/devdvi/CompileFonts OLD_FILES+=usr/share/groff_font/devdvi/DESC OLD_FILES+=usr/share/groff_font/devdvi/EX OLD_FILES+=usr/share/groff_font/devdvi/HB OLD_FILES+=usr/share/groff_font/devdvi/HBEC OLD_FILES+=usr/share/groff_font/devdvi/HBI OLD_FILES+=usr/share/groff_font/devdvi/HBIEC OLD_FILES+=usr/share/groff_font/devdvi/HBITC OLD_FILES+=usr/share/groff_font/devdvi/HBTC OLD_FILES+=usr/share/groff_font/devdvi/HI OLD_FILES+=usr/share/groff_font/devdvi/HIEC OLD_FILES+=usr/share/groff_font/devdvi/HITC OLD_FILES+=usr/share/groff_font/devdvi/HR OLD_FILES+=usr/share/groff_font/devdvi/HREC OLD_FILES+=usr/share/groff_font/devdvi/HRTC OLD_FILES+=usr/share/groff_font/devdvi/MI OLD_FILES+=usr/share/groff_font/devdvi/Makefile OLD_FILES+=usr/share/groff_font/devdvi/S OLD_FILES+=usr/share/groff_font/devdvi/SA OLD_FILES+=usr/share/groff_font/devdvi/SB OLD_FILES+=usr/share/groff_font/devdvi/SC OLD_FILES+=usr/share/groff_font/devdvi/TB OLD_FILES+=usr/share/groff_font/devdvi/TBEC OLD_FILES+=usr/share/groff_font/devdvi/TBI OLD_FILES+=usr/share/groff_font/devdvi/TBIEC OLD_FILES+=usr/share/groff_font/devdvi/TBITC OLD_FILES+=usr/share/groff_font/devdvi/TBTC OLD_FILES+=usr/share/groff_font/devdvi/TI OLD_FILES+=usr/share/groff_font/devdvi/TIEC OLD_FILES+=usr/share/groff_font/devdvi/TITC OLD_FILES+=usr/share/groff_font/devdvi/TR OLD_FILES+=usr/share/groff_font/devdvi/TREC OLD_FILES+=usr/share/groff_font/devdvi/TRTC OLD_FILES+=usr/share/groff_font/devdvi/ec.map OLD_FILES+=usr/share/groff_font/devdvi/msam.map OLD_FILES+=usr/share/groff_font/devdvi/msbm.map OLD_FILES+=usr/share/groff_font/devdvi/tc.map OLD_FILES+=usr/share/groff_font/devdvi/texb.map OLD_FILES+=usr/share/groff_font/devdvi/texex.map OLD_FILES+=usr/share/groff_font/devdvi/texi.map OLD_FILES+=usr/share/groff_font/devdvi/texmi.map OLD_FILES+=usr/share/groff_font/devdvi/texr.map OLD_FILES+=usr/share/groff_font/devdvi/texsy.map OLD_FILES+=usr/share/groff_font/devdvi/textex.map OLD_FILES+=usr/share/groff_font/devdvi/textt.map OLD_DIRS+=usr/share/groff_font/devdvi OLD_FILES+=usr/share/groff_font/devhtml/B OLD_FILES+=usr/share/groff_font/devhtml/BI OLD_FILES+=usr/share/groff_font/devhtml/CB OLD_FILES+=usr/share/groff_font/devhtml/CBI OLD_FILES+=usr/share/groff_font/devhtml/CI OLD_FILES+=usr/share/groff_font/devhtml/CR OLD_FILES+=usr/share/groff_font/devhtml/DESC OLD_FILES+=usr/share/groff_font/devhtml/I OLD_FILES+=usr/share/groff_font/devhtml/R OLD_FILES+=usr/share/groff_font/devhtml/S OLD_DIRS+=usr/share/groff_font/devhtml OLD_FILES+=usr/share/groff_font/devkoi8-r/B OLD_FILES+=usr/share/groff_font/devkoi8-r/BI OLD_FILES+=usr/share/groff_font/devkoi8-r/CW OLD_FILES+=usr/share/groff_font/devkoi8-r/DESC OLD_FILES+=usr/share/groff_font/devkoi8-r/I OLD_FILES+=usr/share/groff_font/devkoi8-r/L OLD_FILES+=usr/share/groff_font/devkoi8-r/R OLD_FILES+=usr/share/groff_font/devkoi8-r/S OLD_DIRS+=usr/share/groff_font/devkoi8-r OLD_FILES+=usr/share/groff_font/devlatin1/B OLD_FILES+=usr/share/groff_font/devlatin1/BI OLD_FILES+=usr/share/groff_font/devlatin1/CW OLD_FILES+=usr/share/groff_font/devlatin1/DESC OLD_FILES+=usr/share/groff_font/devlatin1/I OLD_FILES+=usr/share/groff_font/devlatin1/L OLD_FILES+=usr/share/groff_font/devlatin1/R OLD_FILES+=usr/share/groff_font/devlatin1/S OLD_DIRS+=usr/share/groff_font/devlatin1 OLD_FILES+=usr/share/groff_font/devlbp/CB OLD_FILES+=usr/share/groff_font/devlbp/CI OLD_FILES+=usr/share/groff_font/devlbp/CR OLD_FILES+=usr/share/groff_font/devlbp/DESC OLD_FILES+=usr/share/groff_font/devlbp/EB OLD_FILES+=usr/share/groff_font/devlbp/EI OLD_FILES+=usr/share/groff_font/devlbp/ER OLD_FILES+=usr/share/groff_font/devlbp/HB OLD_FILES+=usr/share/groff_font/devlbp/HBI OLD_FILES+=usr/share/groff_font/devlbp/HI OLD_FILES+=usr/share/groff_font/devlbp/HNB OLD_FILES+=usr/share/groff_font/devlbp/HNBI OLD_FILES+=usr/share/groff_font/devlbp/HNI OLD_FILES+=usr/share/groff_font/devlbp/HNR OLD_FILES+=usr/share/groff_font/devlbp/HR OLD_FILES+=usr/share/groff_font/devlbp/TB OLD_FILES+=usr/share/groff_font/devlbp/TBI OLD_FILES+=usr/share/groff_font/devlbp/TI OLD_FILES+=usr/share/groff_font/devlbp/TR OLD_DIRS+=usr/share/groff_font/devlbp OLD_FILES+=usr/share/groff_font/devlj4/AB OLD_FILES+=usr/share/groff_font/devlj4/ABI OLD_FILES+=usr/share/groff_font/devlj4/AI OLD_FILES+=usr/share/groff_font/devlj4/ALBB OLD_FILES+=usr/share/groff_font/devlj4/ALBR OLD_FILES+=usr/share/groff_font/devlj4/AOB OLD_FILES+=usr/share/groff_font/devlj4/AOI OLD_FILES+=usr/share/groff_font/devlj4/AOR OLD_FILES+=usr/share/groff_font/devlj4/AR OLD_FILES+=usr/share/groff_font/devlj4/CB OLD_FILES+=usr/share/groff_font/devlj4/CBI OLD_FILES+=usr/share/groff_font/devlj4/CI OLD_FILES+=usr/share/groff_font/devlj4/CLARENDON OLD_FILES+=usr/share/groff_font/devlj4/CORONET OLD_FILES+=usr/share/groff_font/devlj4/CR OLD_FILES+=usr/share/groff_font/devlj4/DESC OLD_FILES+=usr/share/groff_font/devlj4/GB OLD_FILES+=usr/share/groff_font/devlj4/GBI OLD_FILES+=usr/share/groff_font/devlj4/GI OLD_FILES+=usr/share/groff_font/devlj4/GR OLD_FILES+=usr/share/groff_font/devlj4/LGB OLD_FILES+=usr/share/groff_font/devlj4/LGI OLD_FILES+=usr/share/groff_font/devlj4/LGR OLD_FILES+=usr/share/groff_font/devlj4/MARIGOLD OLD_FILES+=usr/share/groff_font/devlj4/OB OLD_FILES+=usr/share/groff_font/devlj4/OBI OLD_FILES+=usr/share/groff_font/devlj4/OI OLD_FILES+=usr/share/groff_font/devlj4/OR OLD_FILES+=usr/share/groff_font/devlj4/S OLD_FILES+=usr/share/groff_font/devlj4/SYMBOL OLD_FILES+=usr/share/groff_font/devlj4/TB OLD_FILES+=usr/share/groff_font/devlj4/TBI OLD_FILES+=usr/share/groff_font/devlj4/TI OLD_FILES+=usr/share/groff_font/devlj4/TNRB OLD_FILES+=usr/share/groff_font/devlj4/TNRBI OLD_FILES+=usr/share/groff_font/devlj4/TNRI OLD_FILES+=usr/share/groff_font/devlj4/TNRR OLD_FILES+=usr/share/groff_font/devlj4/TR OLD_FILES+=usr/share/groff_font/devlj4/UB OLD_FILES+=usr/share/groff_font/devlj4/UBI OLD_FILES+=usr/share/groff_font/devlj4/UCB OLD_FILES+=usr/share/groff_font/devlj4/UCBI OLD_FILES+=usr/share/groff_font/devlj4/UCI OLD_FILES+=usr/share/groff_font/devlj4/UCR OLD_FILES+=usr/share/groff_font/devlj4/UI OLD_FILES+=usr/share/groff_font/devlj4/UR OLD_FILES+=usr/share/groff_font/devlj4/WINGDINGS OLD_DIRS+=usr/share/groff_font/devlj4 OLD_FILES+=usr/share/groff_font/devps/AB OLD_FILES+=usr/share/groff_font/devps/ABI OLD_FILES+=usr/share/groff_font/devps/AI OLD_FILES+=usr/share/groff_font/devps/AR OLD_FILES+=usr/share/groff_font/devps/BMB OLD_FILES+=usr/share/groff_font/devps/BMBI OLD_FILES+=usr/share/groff_font/devps/BMI OLD_FILES+=usr/share/groff_font/devps/BMR OLD_FILES+=usr/share/groff_font/devps/CB OLD_FILES+=usr/share/groff_font/devps/CBI OLD_FILES+=usr/share/groff_font/devps/CI OLD_FILES+=usr/share/groff_font/devps/CR OLD_FILES+=usr/share/groff_font/devps/DESC OLD_FILES+=usr/share/groff_font/devps/EURO OLD_FILES+=usr/share/groff_font/devps/HB OLD_FILES+=usr/share/groff_font/devps/HBI OLD_FILES+=usr/share/groff_font/devps/HI OLD_FILES+=usr/share/groff_font/devps/HNB OLD_FILES+=usr/share/groff_font/devps/HNBI OLD_FILES+=usr/share/groff_font/devps/HNI OLD_FILES+=usr/share/groff_font/devps/HNR OLD_FILES+=usr/share/groff_font/devps/HR OLD_FILES+=usr/share/groff_font/devps/Makefile OLD_FILES+=usr/share/groff_font/devps/NB OLD_FILES+=usr/share/groff_font/devps/NBI OLD_FILES+=usr/share/groff_font/devps/NI OLD_FILES+=usr/share/groff_font/devps/NR OLD_FILES+=usr/share/groff_font/devps/PB OLD_FILES+=usr/share/groff_font/devps/PBI OLD_FILES+=usr/share/groff_font/devps/PI OLD_FILES+=usr/share/groff_font/devps/PR OLD_FILES+=usr/share/groff_font/devps/S OLD_FILES+=usr/share/groff_font/devps/SS OLD_FILES+=usr/share/groff_font/devps/TB OLD_FILES+=usr/share/groff_font/devps/TBI OLD_FILES+=usr/share/groff_font/devps/TI OLD_FILES+=usr/share/groff_font/devps/TR OLD_FILES+=usr/share/groff_font/devps/ZCMI OLD_FILES+=usr/share/groff_font/devps/ZD OLD_FILES+=usr/share/groff_font/devps/ZDR OLD_FILES+=usr/share/groff_font/devps/afmname OLD_FILES+=usr/share/groff_font/devps/dingbats.map OLD_FILES+=usr/share/groff_font/devps/dingbats.rmap OLD_FILES+=usr/share/groff_font/devps/download OLD_FILES+=usr/share/groff_font/devps/freeeuro.pfa OLD_FILES+=usr/share/groff_font/devps/lgreekmap OLD_FILES+=usr/share/groff_font/devps/prologue OLD_FILES+=usr/share/groff_font/devps/symbol.sed OLD_FILES+=usr/share/groff_font/devps/symbolchars OLD_FILES+=usr/share/groff_font/devps/symbolsl.afm OLD_FILES+=usr/share/groff_font/devps/symbolsl.pfa OLD_FILES+=usr/share/groff_font/devps/text.enc OLD_FILES+=usr/share/groff_font/devps/textmap OLD_FILES+=usr/share/groff_font/devps/zapfdr.pfa OLD_DIRS+=usr/share/groff_font/devps OLD_FILES+=usr/share/groff_font/devutf8/B OLD_FILES+=usr/share/groff_font/devutf8/BI OLD_FILES+=usr/share/groff_font/devutf8/CW OLD_FILES+=usr/share/groff_font/devutf8/DESC OLD_FILES+=usr/share/groff_font/devutf8/I OLD_FILES+=usr/share/groff_font/devutf8/L OLD_FILES+=usr/share/groff_font/devutf8/R OLD_FILES+=usr/share/groff_font/devutf8/S OLD_DIRS+=usr/share/groff_font/devutf8 OLD_DIRS+=usr/share/groff_font OLD_FILES+=usr/share/info/groff.info.gz OLD_FILES+=usr/share/man/man1/addftinfo.1.gz OLD_FILES+=usr/share/man/man1/afmtodit.1.gz OLD_FILES+=usr/share/man/man1/eqn.1.gz OLD_FILES+=usr/share/man/man1/grn.1.gz OLD_FILES+=usr/share/man/man1/grodvi.1.gz OLD_FILES+=usr/share/man/man1/groff.1.gz OLD_FILES+=usr/share/man/man1/grog.1.gz OLD_FILES+=usr/share/man/man1/grolbp.1.gz OLD_FILES+=usr/share/man/man1/grolj4.1.gz OLD_FILES+=usr/share/man/man1/grops.1.gz OLD_FILES+=usr/share/man/man1/grotty.1.gz OLD_FILES+=usr/share/man/man1/hpftodit.1.gz OLD_FILES+=usr/share/man/man1/indxbib.1.gz OLD_FILES+=usr/share/man/man1/lkbib.1.gz OLD_FILES+=usr/share/man/man1/lookbib.1.gz OLD_FILES+=usr/share/man/man1/mmroff.1.gz OLD_FILES+=usr/share/man/man1/neqn.1.gz OLD_FILES+=usr/share/man/man1/nroff.1.gz OLD_FILES+=usr/share/man/man1/pfbtops.1.gz OLD_FILES+=usr/share/man/man1/pic.1.gz OLD_FILES+=usr/share/man/man1/psroff.1.gz OLD_FILES+=usr/share/man/man1/refer.1.gz OLD_FILES+=usr/share/man/man1/soelim.1.gz OLD_FILES+=usr/share/man/man1/tbl.1.gz OLD_FILES+=usr/share/man/man1/tfmtodit.1.gz OLD_FILES+=usr/share/man/man1/troff.1.gz OLD_FILES+=usr/share/man/man1/vgrind.1.gz OLD_FILES+=usr/share/man/man5/groff_font.5.gz OLD_FILES+=usr/share/man/man5/groff_out.5.gz OLD_FILES+=usr/share/man/man5/groff_tmac.5.gz OLD_FILES+=usr/share/man/man5/lj4_font.5.gz OLD_FILES+=usr/share/man/man5/tmac.5.gz OLD_FILES+=usr/share/man/man5/vgrindefs.5.gz OLD_FILES+=usr/share/man/man7/ditroff.7.gz OLD_FILES+=usr/share/man/man7/groff.7.gz OLD_FILES+=usr/share/man/man7/groff_char.7.gz OLD_FILES+=usr/share/man/man7/groff_diff.7.gz OLD_FILES+=usr/share/man/man7/groff_man.7.gz OLD_FILES+=usr/share/man/man7/groff_mdoc.7.gz OLD_FILES+=usr/share/man/man7/groff_me.7.gz OLD_FILES+=usr/share/man/man7/groff_mm.7.gz OLD_FILES+=usr/share/man/man7/groff_mmse.7.gz OLD_FILES+=usr/share/man/man7/groff_ms.7.gz OLD_FILES+=usr/share/man/man7/groff_trace.7.gz OLD_FILES+=usr/share/man/man7/groff_www.7.gz OLD_FILES+=usr/share/man/man7/mdoc.samples.7.gz OLD_FILES+=usr/share/man/man7/me.7.gz OLD_FILES+=usr/share/man/man7/mm.7.gz OLD_FILES+=usr/share/man/man7/mmse.7.gz OLD_FILES+=usr/share/man/man7/ms.7.gz OLD_FILES+=usr/share/man/man7/orig_me.7.gz OLD_FILES+=usr/share/man/man7/roff.7.gz OLD_FILES+=usr/share/me/acm.me OLD_FILES+=usr/share/me/chars.me OLD_FILES+=usr/share/me/deltext.me OLD_FILES+=usr/share/me/eqn.me OLD_FILES+=usr/share/me/float.me OLD_FILES+=usr/share/me/footnote.me OLD_FILES+=usr/share/me/index.me OLD_FILES+=usr/share/me/letterhead.me OLD_FILES+=usr/share/me/local.me OLD_FILES+=usr/share/me/null.me OLD_FILES+=usr/share/me/refer.me OLD_FILES+=usr/share/me/revisions OLD_FILES+=usr/share/me/sh.me OLD_FILES+=usr/share/me/tbl.me OLD_FILES+=usr/share/me/thesis.me OLD_DIRS+=usr/share/me OLD_FILES+=usr/share/misc/vgrindefs OLD_FILES+=usr/share/misc/vgrindefs.db OLD_FILES+=usr/share/tmac/X.tmac OLD_FILES+=usr/share/tmac/Xps.tmac OLD_FILES+=usr/share/tmac/a4.tmac OLD_FILES+=usr/share/tmac/an-old.tmac OLD_FILES+=usr/share/tmac/an.tmac OLD_FILES+=usr/share/tmac/andoc.tmac OLD_FILES+=usr/share/tmac/composite.tmac OLD_FILES+=usr/share/tmac/cp1047.tmac OLD_FILES+=usr/share/tmac/devtag.tmac OLD_FILES+=usr/share/tmac/doc.tmac OLD_FILES+=usr/share/tmac/dvi.tmac OLD_FILES+=usr/share/tmac/e.tmac OLD_FILES+=usr/share/tmac/ec.tmac OLD_FILES+=usr/share/tmac/eqnrc OLD_FILES+=usr/share/tmac/europs.tmac OLD_FILES+=usr/share/tmac/html-end.tmac OLD_FILES+=usr/share/tmac/html.tmac OLD_FILES+=usr/share/tmac/hyphen.ru OLD_FILES+=usr/share/tmac/hyphen.us OLD_FILES+=usr/share/tmac/hyphenex.us OLD_FILES+=usr/share/tmac/koi8-r.tmac OLD_FILES+=usr/share/tmac/latin1.tmac OLD_FILES+=usr/share/tmac/latin2.tmac OLD_FILES+=usr/share/tmac/latin9.tmac OLD_FILES+=usr/share/tmac/lbp.tmac OLD_FILES+=usr/share/tmac/lj4.tmac OLD_FILES+=usr/share/tmac/m.tmac OLD_FILES+=usr/share/tmac/man.local OLD_FILES+=usr/share/tmac/man.tmac OLD_FILES+=usr/share/tmac/mandoc.tmac OLD_FILES+=usr/share/tmac/mdoc.local OLD_FILES+=usr/share/tmac/mdoc.tmac OLD_FILES+=usr/share/tmac/mdoc/doc-common OLD_FILES+=usr/share/tmac/mdoc/doc-ditroff OLD_FILES+=usr/share/tmac/mdoc/doc-nroff OLD_FILES+=usr/share/tmac/mdoc/doc-syms OLD_FILES+=usr/share/tmac/mdoc/fr.ISO8859-1 OLD_FILES+=usr/share/tmac/mdoc/ru.KOI8-R OLD_DIRS+=usr/share/tmac/mdoc OLD_FILES+=usr/share/tmac/me.tmac OLD_FILES+=usr/share/tmac/mm/0.MT OLD_FILES+=usr/share/tmac/mm/4.MT OLD_FILES+=usr/share/tmac/mm/5.MT OLD_FILES+=usr/share/tmac/mm/locale OLD_FILES+=usr/share/tmac/mm/mm.tmac OLD_FILES+=usr/share/tmac/mm/mmse.tmac OLD_FILES+=usr/share/tmac/mm/ms.cov OLD_FILES+=usr/share/tmac/mm/se_locale OLD_FILES+=usr/share/tmac/mm/se_ms.cov OLD_DIRS+=usr/share/tmac/mm OLD_FILES+=usr/share/tmac/ms.tmac OLD_FILES+=usr/share/tmac/mse.tmac OLD_FILES+=usr/share/tmac/papersize.tmac OLD_FILES+=usr/share/tmac/pic.tmac OLD_FILES+=usr/share/tmac/ps.tmac OLD_FILES+=usr/share/tmac/psatk.tmac OLD_FILES+=usr/share/tmac/psold.tmac OLD_FILES+=usr/share/tmac/pspic.tmac OLD_FILES+=usr/share/tmac/s.tmac OLD_FILES+=usr/share/tmac/safer.tmac OLD_FILES+=usr/share/tmac/tmac.orig_me OLD_FILES+=usr/share/tmac/tmac.vgrind OLD_FILES+=usr/share/tmac/trace.tmac OLD_FILES+=usr/share/tmac/troffrc OLD_FILES+=usr/share/tmac/troffrc-end OLD_FILES+=usr/share/tmac/tty-char.tmac OLD_FILES+=usr/share/tmac/tty.tmac OLD_FILES+=usr/share/tmac/unicode.tmac OLD_FILES+=usr/share/tmac/www.tmac OLD_DIRS+=usr/share/tmac .endif .if ${MK_GSSAPI} == no OLD_FILES+=etc/gss/mech OLD_FILES+=etc/gss/qop OLD_DIRS+=etc/gss OLD_FILES+=etc/rc.d/gssd OLD_FILES+=usr/include/gssapi/gssapi.h OLD_DIRS+=usr/include/gssapi OLD_FILES+=usr/include/gssapi.h OLD_FILES+=usr/lib/libgssapi.a OLD_FILES+=usr/lib/libgssapi.so OLD_LIBS+=usr/lib/libgssapi.so.10 OLD_FILES+=usr/lib/libgssapi_p.a OLD_FILES+=usr/lib/librpcsec_gss.a OLD_FILES+=usr/lib/librpcsec_gss.so OLD_LIBS+=usr/lib/librpcsec_gss.so.1 .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libgssapi.a OLD_FILES+=usr/lib32/libgssapi.so OLD_LIBS+=usr/lib32/libgssapi.so.10 OLD_FILES+=usr/lib32/libgssapi_p.a OLD_FILES+=usr/lib32/librpcsec_gss.a OLD_FILES+=usr/lib32/librpcsec_gss.so OLD_LIBS+=usr/lib32/librpcsec_gss.so.1 .endif OLD_FILES+=usr/sbin/gssd OLD_FILES+=usr/share/man/man3/gss_accept_sec_context.3.gz OLD_FILES+=usr/share/man/man3/gss_acquire_cred.3.gz OLD_FILES+=usr/share/man/man3/gss_add_cred.3.gz OLD_FILES+=usr/share/man/man3/gss_add_oid_set_member.3.gz OLD_FILES+=usr/share/man/man3/gss_canonicalize_name.3.gz OLD_FILES+=usr/share/man/man3/gss_compare_name.3.gz OLD_FILES+=usr/share/man/man3/gss_context_time.3.gz OLD_FILES+=usr/share/man/man3/gss_create_empty_oid_set.3.gz OLD_FILES+=usr/share/man/man3/gss_delete_sec_context.3.gz OLD_FILES+=usr/share/man/man3/gss_display_name.3.gz OLD_FILES+=usr/share/man/man3/gss_display_status.3.gz OLD_FILES+=usr/share/man/man3/gss_duplicate_name.3.gz OLD_FILES+=usr/share/man/man3/gss_export_name.3.gz OLD_FILES+=usr/share/man/man3/gss_export_sec_context.3.gz OLD_FILES+=usr/share/man/man3/gss_get_mic.3.gz OLD_FILES+=usr/share/man/man3/gss_import_name.3.gz OLD_FILES+=usr/share/man/man3/gss_import_sec_context.3.gz OLD_FILES+=usr/share/man/man3/gss_indicate_mechs.3.gz OLD_FILES+=usr/share/man/man3/gss_init_sec_context.3.gz OLD_FILES+=usr/share/man/man3/gss_inquire_context.3.gz OLD_FILES+=usr/share/man/man3/gss_inquire_cred.3.gz OLD_FILES+=usr/share/man/man3/gss_inquire_cred_by_mech.3.gz OLD_FILES+=usr/share/man/man3/gss_inquire_mechs_for_name.3.gz OLD_FILES+=usr/share/man/man3/gss_inquire_names_for_mech.3.gz OLD_FILES+=usr/share/man/man3/gss_process_context_token.3.gz OLD_FILES+=usr/share/man/man3/gss_release_buffer.3.gz OLD_FILES+=usr/share/man/man3/gss_release_cred.3.gz OLD_FILES+=usr/share/man/man3/gss_release_name.3.gz OLD_FILES+=usr/share/man/man3/gss_release_oid_set.3.gz OLD_FILES+=usr/share/man/man3/gss_seal.3.gz OLD_FILES+=usr/share/man/man3/gss_sign.3.gz OLD_FILES+=usr/share/man/man3/gss_test_oid_set_member.3.gz OLD_FILES+=usr/share/man/man3/gss_unseal.3.gz OLD_FILES+=usr/share/man/man3/gss_unwrap.3.gz OLD_FILES+=usr/share/man/man3/gss_verify.3.gz OLD_FILES+=usr/share/man/man3/gss_verify_mic.3.gz OLD_FILES+=usr/share/man/man3/gss_wrap.3.gz OLD_FILES+=usr/share/man/man3/gss_wrap_size_limit.3.gz OLD_FILES+=usr/share/man/man3/gssapi.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_get_error.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_get_mech_info.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_get_mechanisms.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_get_principal_name.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_get_versions.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_getcred.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_is_installed.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_max_data_length.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_mech_to_oid.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_oid_to_mech.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_qop_to_num.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_seccreate.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_set_callback.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_set_defaults.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_set_svc_name.3.gz OLD_FILES+=usr/share/man/man3/rpc_gss_svc_max_data_length.3.gz OLD_FILES+=usr/share/man/man3/rpcsec_gss.3.gz OLD_FILES+=usr/share/man/man5/mech.5.gz OLD_FILES+=usr/share/man/man5/qop.5.gz OLD_FILES+=usr/share/man/man8/gssd.8.gz .endif .if ${MK_HAST} == no OLD_FILES+=sbin/hastctl OLD_FILES+=sbin/hastd OLD_FILES+=usr/share/examples/hast/ucarp.sh OLD_FILES+=usr/share/examples/hast/ucarp_down.sh OLD_FILES+=usr/share/examples/hast/ucarp_up.sh OLD_FILES+=usr/share/examples/hast/vip-down.sh OLD_FILES+=usr/share/examples/hast/vip-up.sh OLD_FILES+=usr/share/man/man5/hast.conf.5.gz OLD_FILES+=usr/share/man/man8/hastctl.8.gz OLD_FILES+=usr/share/man/man8/hastd.8.gz OLD_DIRS+=usr/share/examples/hast .endif .if ${MK_HESIOD} == no OLD_FILES+=usr/bin/hesinfo OLD_FILES+=usr/include/hesiod.h OLD_FILES+=usr/share/man/man1/hesinfo.1.gz OLD_FILES+=usr/share/man/man3/hesiod.3.gz OLD_FILES+=usr/share/man/man5/hesiod.conf.5.gz .endif -#.if ${MK_HTML} == no -# to be filled in -#.endif +.if ${MK_HTML} == no +OLD_FILES+=usr/share/doc/ncurses/hackguide.html +OLD_FILES+=usr/share/doc/ncurses/ncurses-intro.html +OLD_FILES+=usr/share/doc/ntp/accopt.html +OLD_FILES+=usr/share/doc/ntp/assoc.html +OLD_FILES+=usr/share/doc/ntp/audio.html +OLD_FILES+=usr/share/doc/ntp/authopt.html +OLD_FILES+=usr/share/doc/ntp/build.html +OLD_FILES+=usr/share/doc/ntp/clockopt.html +OLD_FILES+=usr/share/doc/ntp/config.html +OLD_FILES+=usr/share/doc/ntp/confopt.html +OLD_FILES+=usr/share/doc/ntp/copyright.html +OLD_FILES+=usr/share/doc/ntp/debug.html +OLD_FILES+=usr/share/doc/ntp/driver1.html +OLD_FILES+=usr/share/doc/ntp/driver10.html +OLD_FILES+=usr/share/doc/ntp/driver11.html +OLD_FILES+=usr/share/doc/ntp/driver12.html +OLD_FILES+=usr/share/doc/ntp/driver16.html +OLD_FILES+=usr/share/doc/ntp/driver18.html +OLD_FILES+=usr/share/doc/ntp/driver19.html +OLD_FILES+=usr/share/doc/ntp/driver2.html +OLD_FILES+=usr/share/doc/ntp/driver20.html +OLD_FILES+=usr/share/doc/ntp/driver22.html +OLD_FILES+=usr/share/doc/ntp/driver26.html +OLD_FILES+=usr/share/doc/ntp/driver27.html +OLD_FILES+=usr/share/doc/ntp/driver28.html +OLD_FILES+=usr/share/doc/ntp/driver29.html +OLD_FILES+=usr/share/doc/ntp/driver3.html +OLD_FILES+=usr/share/doc/ntp/driver30.html +OLD_FILES+=usr/share/doc/ntp/driver32.html +OLD_FILES+=usr/share/doc/ntp/driver33.html +OLD_FILES+=usr/share/doc/ntp/driver34.html +OLD_FILES+=usr/share/doc/ntp/driver35.html +OLD_FILES+=usr/share/doc/ntp/driver36.html +OLD_FILES+=usr/share/doc/ntp/driver37.html +OLD_FILES+=usr/share/doc/ntp/driver4.html +OLD_FILES+=usr/share/doc/ntp/driver5.html +OLD_FILES+=usr/share/doc/ntp/driver6.html +OLD_FILES+=usr/share/doc/ntp/driver7.html +OLD_FILES+=usr/share/doc/ntp/driver8.html +OLD_FILES+=usr/share/doc/ntp/driver9.html +OLD_FILES+=usr/share/doc/ntp/extern.html +OLD_FILES+=usr/share/doc/ntp/hints.html +OLD_FILES+=usr/share/doc/ntp/howto.html +OLD_FILES+=usr/share/doc/ntp/index.html +OLD_FILES+=usr/share/doc/ntp/kern.html +OLD_FILES+=usr/share/doc/ntp/ldisc.html +OLD_FILES+=usr/share/doc/ntp/measure.html +OLD_FILES+=usr/share/doc/ntp/miscopt.html +OLD_FILES+=usr/share/doc/ntp/monopt.html +OLD_FILES+=usr/share/doc/ntp/mx4200data.html +OLD_FILES+=usr/share/doc/ntp/notes.html +OLD_FILES+=usr/share/doc/ntp/ntpd.html +OLD_FILES+=usr/share/doc/ntp/ntpdate.html +OLD_FILES+=usr/share/doc/ntp/ntpdc.html +OLD_FILES+=usr/share/doc/ntp/ntpq.html +OLD_FILES+=usr/share/doc/ntp/ntptime.html +OLD_FILES+=usr/share/doc/ntp/ntptrace.html +OLD_FILES+=usr/share/doc/ntp/parsedata.html +OLD_FILES+=usr/share/doc/ntp/parsenew.html +OLD_FILES+=usr/share/doc/ntp/patches.html +OLD_FILES+=usr/share/doc/ntp/porting.html +OLD_FILES+=usr/share/doc/ntp/pps.html +OLD_FILES+=usr/share/doc/ntp/prefer.html +OLD_FILES+=usr/share/doc/ntp/quick.html +OLD_FILES+=usr/share/doc/ntp/rdebug.html +OLD_FILES+=usr/share/doc/ntp/refclock.html +OLD_FILES+=usr/share/doc/ntp/release.html +OLD_FILES+=usr/share/doc/ntp/tickadj.html +.endif .if ${MK_ICONV} == no OLD_FILES+=usr/bin/iconv OLD_FILES+=usr/bin/mkcsmapper OLD_FILES+=usr/bin/mkesdb OLD_FILES+=usr/include/_libiconv_compat.h OLD_FILES+=usr/include/iconv.h OLD_FILES+=usr/share/man/man1/iconv.1.gz OLD_FILES+=usr/share/man/man1/mkcsmapper.1.gz OLD_FILES+=usr/share/man/man1/mkesdb.1.gz OLD_FILES+=usr/share/man/man3/__iconv.3.gz OLD_FILES+=usr/share/man/man3/__iconv_free_list.3.gz OLD_FILES+=usr/share/man/man3/__iconv_get_list.3.gz OLD_FILES+=usr/share/man/man3/iconv.3.gz OLD_FILES+=usr/share/man/man3/iconv_canonicalize.3.gz OLD_FILES+=usr/share/man/man3/iconv_close.3.gz OLD_FILES+=usr/share/man/man3/iconv_open.3.gz OLD_FILES+=usr/share/man/man3/iconv_open_into.3.gz OLD_FILES+=usr/share/man/man3/iconvctl.3.gz OLD_FILES+=usr/share/man/man3/iconvlist.3.gz .endif .if ${MK_INET6} == no OLD_FILES+=etc/rc.d/ip6addrctl OLD_FILES+=etc/rc.d/mroute6d OLD_FILES+=etc/rc.d/route6d OLD_FILES+=etc/rc.d/rtadvd OLD_FILES+=etc/rc.d/rtsold OLD_FILES+=etc/rc.d/stf OLD_FILES+=etc/rc.d/static_ndp OLD_FILES+=sbin/ping6 OLD_FILES+=sbin/rtsol OLD_FILES+=usr/sbin/ip6addrctl OLD_FILES+=usr/sbin/mld6query OLD_FILES+=usr/sbin/ndp OLD_FILES+=usr/sbin/rip6query OLD_FILES+=usr/sbin/route6d OLD_FILES+=usr/sbin/rrenumd OLD_FILES+=usr/sbin/rtadvd OLD_FILES+=usr/sbin/rtsold OLD_FILES+=usr/sbin/traceroute6 OLD_FILES+=usr/share/man/man5/rrenumd.conf.5.gz OLD_FILES+=usr/share/man/man5/rtadvd.conf.5.gz OLD_FILES+=usr/share/man/man8/ip6addrctl.8.gz OLD_FILES+=usr/share/man/man8/mld6query.8.gz OLD_FILES+=usr/share/man/man8/ndp.8.gz OLD_FILES+=usr/share/man/man8/ping6.8.gz OLD_FILES+=usr/share/man/man8/rip6query.8.gz OLD_FILES+=usr/share/man/man8/route6d.8.gz OLD_FILES+=usr/share/man/man8/rrenumd.8.gz OLD_FILES+=usr/share/man/man8/rtadvd.8.gz OLD_FILES+=usr/share/man/man8/rtsol.8.gz OLD_FILES+=usr/share/man/man8/rtsold.8.gz OLD_FILES+=usr/share/man/man8/traceroute6.8.gz .endif .if ${MK_INET6_SUPPORT} == no OLD_FILES+=rescue/ping6 .endif .if ${MK_INETD} == no OLD_FILES+=etc/rc.d/inetd OLD_FILES+=usr/sbin/inetd OLD_FILES+=usr/share/man/man5/inetd.conf.5.gz OLD_FILES+=usr/share/man/man8/inetd.8.gz .endif .if ${MK_INFO} == no OLD_FILES+=usr/bin/info OLD_FILES+=usr/bin/infokey OLD_FILES+=usr/bin/install-info OLD_FILES+=usr/bin/makeinfo OLD_FILES+=usr/bin/texindex OLD_FILES+=usr/share/info/am-utils.info.gz OLD_FILES+=usr/share/info/as-utils.info.gz OLD_FILES+=usr/share/info/binutils.info.gz OLD_FILES+=usr/share/info/com_err.info.gz OLD_FILES+=usr/share/info/diff.info.gz OLD_FILES+=usr/share/info/gdb.info.gz OLD_FILES+=usr/share/info/gdbint.info.gz OLD_FILES+=usr/share/info/gperf.info.gz OLD_FILES+=usr/share/info/grep.info.gz OLD_FILES+=usr/share/info/groff.info.gz OLD_FILES+=usr/share/info/heimdal.info.gz OLD_FILES+=usr/share/info/history.info.gz OLD_FILES+=usr/share/info/info-stnd.info.gz OLD_FILES+=usr/share/info/info.info.gz OLD_FILES+=usr/share/info/ld.info.gz OLD_FILES+=usr/share/info/regex.info.gz OLD_FILES+=usr/share/info/rluserman.info.gz OLD_FILES+=usr/share/info/stabs.info.gz OLD_FILES+=usr/share/info/texinfo.info.gz OLD_FILES+=usr/share/man/man1/info.1.gz OLD_FILES+=usr/share/man/man1/infokey.1.gz OLD_FILES+=usr/share/man/man1/install-info.1.gz OLD_FILES+=usr/share/man/man1/makeinfo.1.gz OLD_FILES+=usr/share/man/man1/texindex.1.gz OLD_FILES+=usr/share/man/man5/info.5.gz OLD_FILES+=usr/share/man/man5/texinfo.5.gz .endif .if ${MK_IPFILTER} == no OLD_FILES+=etc/periodic/security/510.ipfdenied OLD_FILES+=etc/periodic/security/610.ipf6denied OLD_FILES+=rescue/ipf OLD_FILES+=sbin/ipf OLD_FILES+=sbin/ipfs OLD_FILES+=sbin/ipfstat OLD_FILES+=sbin/ipftest OLD_FILES+=sbin/ipmon OLD_FILES+=sbin/ipnat OLD_FILES+=sbin/ippool OLD_FILES+=sbin/ipresend OLD_FILES+=usr/include/netinet/ip_auth.h OLD_FILES+=usr/include/netinet/ip_compat.h OLD_FILES+=usr/include/netinet/ip_fil.h OLD_FILES+=usr/include/netinet/ip_frag.h OLD_FILES+=usr/include/netinet/ip_htable.h OLD_FILES+=usr/include/netinet/ip_lookup.h OLD_FILES+=usr/include/netinet/ip_nat.h OLD_FILES+=usr/include/netinet/ip_pool.h OLD_FILES+=usr/include/netinet/ip_proxy.h OLD_FILES+=usr/include/netinet/ip_rules.h OLD_FILES+=usr/include/netinet/ip_scan.h OLD_FILES+=usr/include/netinet/ip_state.h OLD_FILES+=usr/include/netinet/ip_sync.h OLD_FILES+=usr/include/netinet/ipl.h OLD_FILES+=usr/share/examples/ipfilter/README OLD_FILES+=usr/share/examples/ipfilter/BASIC.NAT OLD_FILES+=usr/share/examples/ipfilter/BASIC_1.FW OLD_FILES+=usr/share/examples/ipfilter/BASIC_2.FW OLD_FILES+=usr/share/examples/ipfilter/example.1 OLD_FILES+=usr/share/examples/ipfilter/example.2 OLD_FILES+=usr/share/examples/ipfilter/example.3 OLD_FILES+=usr/share/examples/ipfilter/example.4 OLD_FILES+=usr/share/examples/ipfilter/example.5 OLD_FILES+=usr/share/examples/ipfilter/example.6 OLD_FILES+=usr/share/examples/ipfilter/example.7 OLD_FILES+=usr/share/examples/ipfilter/example.8 OLD_FILES+=usr/share/examples/ipfilter/example.9 OLD_FILES+=usr/share/examples/ipfilter/example.10 OLD_FILES+=usr/share/examples/ipfilter/example.11 OLD_FILES+=usr/share/examples/ipfilter/example.12 OLD_FILES+=usr/share/examples/ipfilter/example.13 OLD_FILES+=usr/share/examples/ipfilter/example.sr OLD_FILES+=usr/share/examples/ipfilter/firewall OLD_FILES+=usr/share/examples/ipfilter/ftp-proxy OLD_FILES+=usr/share/examples/ipfilter/ftppxy OLD_FILES+=usr/share/examples/ipfilter/nat-setup OLD_FILES+=usr/share/examples/ipfilter/nat.eg OLD_FILES+=usr/share/examples/ipfilter/server OLD_FILES+=usr/share/examples/ipfilter/tcpstate OLD_FILES+=usr/share/examples/ipfilter/example.14 OLD_FILES+=usr/share/examples/ipfilter/firewall.1 OLD_FILES+=usr/share/examples/ipfilter/firewall.2 OLD_FILES+=usr/share/examples/ipfilter/ipf.conf.permissive OLD_FILES+=usr/share/examples/ipfilter/ipf.conf.restrictive OLD_FILES+=usr/share/examples/ipfilter/ipf.conf.sample OLD_FILES+=usr/share/examples/ipfilter/ipnat.conf.sample OLD_FILES+=usr/share/examples/ipfilter/ipf-howto.txt OLD_FILES+=usr/share/examples/ipfilter/examples.txt OLD_FILES+=usr/share/examples/ipfilter/rules.txt OLD_FILES+=usr/share/examples/ipfilter/mkfilters OLD_DIRS+=usr/share/examples/ipfilter OLD_FILES+=usr/share/man/man1/ipftest.1.gz OLD_FILES+=usr/share/man/man1/ipresend.1.gz OLD_FILES+=usr/share/man/man4/ipf.4.gz OLD_FILES+=usr/share/man/man4/ipl.4.gz OLD_FILES+=usr/share/man/man4/ipfilter.4.gz OLD_FILES+=usr/share/man/man4/ipnat.4.gz OLD_FILES+=usr/share/man/man5/ipf.5.gz OLD_FILES+=usr/share/man/man5/ipf.conf.5.gz OLD_FILES+=usr/share/man/man5/ipf6.conf.5.gz OLD_FILES+=usr/share/man/man5/ipnat.5.gz OLD_FILES+=usr/share/man/man5/ipnat.conf.5.gz OLD_FILES+=usr/share/man/man5/ippool.5.gz OLD_FILES+=usr/share/man/man8/ipf.8.gz OLD_FILES+=usr/share/man/man8/ipfs.8.gz OLD_FILES+=usr/share/man/man8/ipfstat.8.gz OLD_FILES+=usr/share/man/man8/ipmon.8.gz OLD_FILES+=usr/share/man/man8/ipnat.8.gz OLD_FILES+=usr/share/man/man8/ippool.8.gz .endif .if ${MK_IPFW} == no OLD_FILES+=etc/periodic/security/500.ipfwdenied OLD_FILES+=etc/periodic/security/550.ipfwlimit OLD_FILES+=sbin/ipfw OLD_FILES+=sbin/natd OLD_FILES+=usr/sbin/ipfwpcap OLD_FILES+=usr/share/man/man8/ipfw.8.gz OLD_FILES+=usr/share/man/man8/ipfwpcap.8.gz OLD_FILES+=usr/share/man/man8/natd.8.gz .endif .if ${MK_ISCSI} == no OLD_FILES+=etc/rc.d/iscsictl OLD_FILES+=etc/rc.d/iscsid OLD_FILES+=sbin/iscontrol OLD_FILES+=usr/bin/iscsictl OLD_FILES+=usr/sbin/iscsid OLD_FILES+=usr/share/man/man4/iscsi.4.gz OLD_FILES+=usr/share/man/man4/iscsi_initiator.4.gz OLD_FILES+=usr/share/man/man5/iscsi.conf.5.gz OLD_FILES+=usr/share/man/man8/iscontrol.8.gz OLD_FILES+=usr/share/man/man8/iscsictl.8.gz OLD_FILES+=usr/share/man/man8/iscsid.8.gz .endif .if ${MK_JAIL} == no OLD_FILES+=etc/rc.d/jail OLD_FILES+=usr/sbin/jail OLD_FILES+=usr/sbin/jexec OLD_FILES+=usr/sbin/jls OLD_FILES+=usr/share/man/man8/jail.8.gz OLD_FILES+=usr/share/man/man8/jexec.8.gz OLD_FILES+=usr/share/man/man8/jls.8.gz .endif .if ${MK_KERBEROS} == no OLD_FILES+=etc/rc.d/ipropd_master OLD_FILES+=etc/rc.d/ipropd_slave OLD_FILES+=usr/bin/compile_et OLD_FILES+=usr/bin/hxtool OLD_FILES+=usr/bin/kadmin OLD_FILES+=usr/bin/kdestroy OLD_FILES+=usr/bin/kf OLD_FILES+=usr/bin/kgetcred OLD_FILES+=usr/bin/kinit OLD_FILES+=usr/bin/klist OLD_FILES+=usr/bin/kpasswd OLD_FILES+=usr/bin/krb5-config OLD_FILES+=usr/bin/ksu OLD_FILES+=usr/bin/kswitch OLD_FILES+=usr/bin/string2key OLD_FILES+=usr/bin/verify_krb5_conf OLD_FILES+=usr/include/asn1-common.h OLD_FILES+=usr/include/asn1_err.h OLD_FILES+=usr/include/base64.h OLD_FILES+=usr/include/cms_asn1.h OLD_FILES+=usr/include/crmf_asn1.h OLD_FILES+=usr/include/der-private.h OLD_FILES+=usr/include/der-protos.h OLD_FILES+=usr/include/der.h OLD_FILES+=usr/include/digest_asn1.h OLD_FILES+=usr/include/getarg.h OLD_FILES+=usr/include/gssapi/gssapi_krb5.h OLD_FILES+=usr/include/hdb-protos.h OLD_FILES+=usr/include/hdb.h OLD_FILES+=usr/include/hdb_asn1.h OLD_FILES+=usr/include/hdb_err.h OLD_FILES+=usr/include/heim_asn1.h OLD_FILES+=usr/include/heim_err.h OLD_FILES+=usr/include/heim_threads.h OLD_FILES+=usr/include/heimbase.h OLD_FILES+=usr/include/heimntlm-protos.h OLD_FILES+=usr/include/heimntlm.h OLD_FILES+=usr/include/hex.h OLD_FILES+=usr/include/hx509-private.h OLD_FILES+=usr/include/hx509-protos.h OLD_FILES+=usr/include/hx509.h OLD_FILES+=usr/include/hx509_err.h OLD_FILES+=usr/include/k524_err.h OLD_FILES+=usr/include/kadm5/admin.h OLD_FILES+=usr/include/kadm5/kadm5-private.h OLD_FILES+=usr/include/kadm5/kadm5-protos.h OLD_FILES+=usr/include/kadm5/kadm5-pwcheck.h OLD_FILES+=usr/include/kadm5/kadm5_err.h OLD_FILES+=usr/include/kadm5/private.h OLD_DIRS+=usr/include/kadm5 OLD_FILES+=usr/include/kafs.h OLD_FILES+=usr/include/kdc-protos.h OLD_FILES+=usr/include/kdc.h OLD_FILES+=usr/include/krb5-private.h OLD_FILES+=usr/include/krb5-protos.h OLD_FILES+=usr/include/krb5-types.h OLD_FILES+=usr/include/krb5.h OLD_FILES+=usr/include/krb5/ccache_plugin.h OLD_FILES+=usr/include/krb5/locate_plugin.h OLD_FILES+=usr/include/krb5/send_to_kdc_plugin.h OLD_FILES+=usr/include/krb5/windc_plugin.h OLD_DIRS+=usr/include/krb5 OLD_FILES+=usr/include/krb5_asn1.h OLD_FILES+=usr/include/krb5_ccapi.h OLD_FILES+=usr/include/krb5_err.h OLD_FILES+=usr/include/kx509_asn1.h OLD_FILES+=usr/include/ntlm_err.h OLD_FILES+=usr/include/ocsp_asn1.h OLD_FILES+=usr/include/parse_bytes.h OLD_FILES+=usr/include/parse_time.h OLD_FILES+=usr/include/parse_units.h OLD_FILES+=usr/include/pkcs10_asn1.h OLD_FILES+=usr/include/pkcs12_asn1.h OLD_FILES+=usr/include/pkcs8_asn1.h OLD_FILES+=usr/include/pkcs9_asn1.h OLD_FILES+=usr/include/pkinit_asn1.h OLD_FILES+=usr/include/resolve.h OLD_FILES+=usr/include/rfc2459_asn1.h OLD_FILES+=usr/include/roken-common.h OLD_FILES+=usr/include/rtbl.h OLD_FILES+=usr/include/wind.h OLD_FILES+=usr/include/wind_err.h OLD_FILES+=usr/include/xdbm.h OLD_FILES+=usr/lib/libasn1.a OLD_FILES+=usr/lib/libasn1.so OLD_LIBS+=usr/lib/libasn1.so.11 OLD_FILES+=usr/lib/libasn1_p.a OLD_FILES+=usr/lib/libcom_err.a OLD_FILES+=usr/lib/libcom_err.so OLD_LIBS+=usr/lib/libcom_err.so.5 OLD_FILES+=usr/lib/libcom_err_p.a OLD_FILES+=usr/lib/libgssapi_krb5.a OLD_FILES+=usr/lib/libgssapi_krb5.so OLD_LIBS+=usr/lib/libgssapi_krb5.so.10 OLD_FILES+=usr/lib/libgssapi_krb5_p.a OLD_FILES+=usr/lib/libgssapi_ntlm.a OLD_FILES+=usr/lib/libgssapi_ntlm.so OLD_LIBS+=usr/lib/libgssapi_ntlm.so.10 OLD_FILES+=usr/lib/libgssapi_ntlm_p.a OLD_FILES+=usr/lib/libgssapi_spnego.a OLD_FILES+=usr/lib/libgssapi_spnego.so OLD_LIBS+=usr/lib/libgssapi_spnego.so.10 OLD_FILES+=usr/lib/libgssapi_spnego_p.a OLD_FILES+=usr/lib/libhdb.a OLD_FILES+=usr/lib/libhdb.so OLD_LIBS+=usr/lib/libhdb.so.11 OLD_FILES+=usr/lib/libhdb_p.a OLD_FILES+=usr/lib/libheimbase.a OLD_FILES+=usr/lib/libheimbase.so OLD_LIBS+=usr/lib/libheimbase.so.11 OLD_FILES+=usr/lib/libheimbase_p.a OLD_FILES+=usr/lib/libheimntlm.a OLD_FILES+=usr/lib/libheimntlm.so OLD_LIBS+=usr/lib/libheimntlm.so.11 OLD_FILES+=usr/lib/libheimntlm_p.a OLD_FILES+=usr/lib/libheimsqlite.a OLD_FILES+=usr/lib/libheimsqlite.so OLD_LIBS+=usr/lib/libheimsqlite.so.11 OLD_FILES+=usr/lib/libheimsqlite_p.a OLD_FILES+=usr/lib/libhx509.a OLD_FILES+=usr/lib/libhx509.so OLD_LIBS+=usr/lib/libhx509.so.11 OLD_FILES+=usr/lib/libhx509_p.a OLD_FILES+=usr/lib/libkadm5clnt.a OLD_FILES+=usr/lib/libkadm5clnt.so OLD_LIBS+=usr/lib/libkadm5clnt.so.11 OLD_FILES+=usr/lib/libkadm5clnt_p.a OLD_FILES+=usr/lib/libkadm5srv.a OLD_FILES+=usr/lib/libkadm5srv.so OLD_LIBS+=usr/lib/libkadm5srv.so.11 OLD_FILES+=usr/lib/libkadm5srv_p.a OLD_FILES+=usr/lib/libkafs5.a OLD_FILES+=usr/lib/libkafs5.so OLD_LIBS+=usr/lib/libkafs5.so.11 OLD_FILES+=usr/lib/libkafs5_p.a OLD_FILES+=usr/lib/libkdc.a OLD_FILES+=usr/lib/libkdc.so OLD_LIBS+=usr/lib/libkdc.so.11 OLD_FILES+=usr/lib/libkdc_p.a OLD_FILES+=usr/lib/libkrb5.a OLD_FILES+=usr/lib/libkrb5.so OLD_LIBS+=usr/lib/libkrb5.so.11 OLD_FILES+=usr/lib/libkrb5_p.a OLD_FILES+=usr/lib/libroken.a OLD_FILES+=usr/lib/libroken.so OLD_LIBS+=usr/lib/libroken.so.11 OLD_FILES+=usr/lib/libroken_p.a OLD_FILES+=usr/lib/libwind.a OLD_FILES+=usr/lib/libwind.so OLD_LIBS+=usr/lib/libwind.so.11 OLD_FILES+=usr/lib/libwind_p.a OLD_FILES+=usr/lib/pam_krb5.so OLD_LIBS+=usr/lib/pam_krb5.so.5 OLD_FILES+=usr/lib/pam_ksu.so OLD_LIBS+=usr/lib/pam_ksu.so.5 OLD_FILES+=usr/lib/private/libheimipcc.a OLD_FILES+=usr/lib/private/libheimipcc.so OLD_LIBS+=usr/lib/private/libheimipcc.so.11 OLD_FILES+=usr/lib/private/libheimipcc_p.a OLD_FILES+=usr/lib/private/libheimipcs.a OLD_FILES+=usr/lib/private/libheimipcs.so OLD_LIBS+=usr/lib/private/libheimipcs.so.11 OLD_FILES+=usr/lib/private/libheimipcs_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libasn1.a OLD_FILES+=usr/lib32/libasn1.so OLD_LIBS+=usr/lib32/libasn1.so.11 OLD_FILES+=usr/lib32/libasn1_p.a OLD_FILES+=usr/lib32/libgssapi_krb5.a OLD_FILES+=usr/lib32/libgssapi_krb5.so OLD_LIBS+=usr/lib32/libgssapi_krb5.so.10 OLD_FILES+=usr/lib32/libgssapi_krb5_p.a OLD_FILES+=usr/lib32/libgssapi_ntlm.a OLD_FILES+=usr/lib32/libgssapi_ntlm.so OLD_LIBS+=usr/lib32/libgssapi_ntlm.so.10 OLD_FILES+=usr/lib32/libgssapi_ntlm_p.a OLD_FILES+=usr/lib32/libgssapi_spnego.a OLD_FILES+=usr/lib32/libgssapi_spnego.so OLD_LIBS+=usr/lib32/libgssapi_spnego.so.10 OLD_FILES+=usr/lib32/libgssapi_spnego_p.a OLD_FILES+=usr/lib32/libhdb.a OLD_FILES+=usr/lib32/libhdb.so OLD_LIBS+=usr/lib32/libhdb.so.11 OLD_FILES+=usr/lib32/libhdb_p.a OLD_FILES+=usr/lib32/libheimbase.a OLD_FILES+=usr/lib32/libheimbase.so OLD_LIBS+=usr/lib32/libheimbase.so.11 OLD_FILES+=usr/lib32/libheimbase_p.a OLD_FILES+=usr/lib32/libheimntlm.a OLD_FILES+=usr/lib32/libheimntlm.so OLD_LIBS+=usr/lib32/libheimntlm.so.11 OLD_FILES+=usr/lib32/libheimntlm_p.a OLD_FILES+=usr/lib32/libheimsqlite.a OLD_FILES+=usr/lib32/libheimsqlite.so OLD_LIBS+=usr/lib32/libheimsqlite.so.11 OLD_FILES+=usr/lib32/libheimsqlite_p.a OLD_FILES+=usr/lib32/libhx509.a OLD_FILES+=usr/lib32/libhx509.so OLD_LIBS+=usr/lib32/libhx509.so.11 OLD_FILES+=usr/lib32/libhx509_p.a OLD_FILES+=usr/lib32/libkadm5clnt.a OLD_FILES+=usr/lib32/libkadm5clnt.so OLD_LIBS+=usr/lib32/libkadm5clnt.so.11 OLD_FILES+=usr/lib32/libkadm5clnt_p.a OLD_FILES+=usr/lib32/libkadm5srv.a OLD_FILES+=usr/lib32/libkadm5srv.so OLD_LIBS+=usr/lib32/libkadm5srv.so.11 OLD_FILES+=usr/lib32/libkadm5srv_p.a OLD_FILES+=usr/lib32/libkafs5.a OLD_FILES+=usr/lib32/libkafs5.so OLD_LIBS+=usr/lib32/libkafs5.so.11 OLD_FILES+=usr/lib32/libkafs5_p.a OLD_FILES+=usr/lib32/libkdc.a OLD_FILES+=usr/lib32/libkdc.so OLD_LIBS+=usr/lib32/libkdc.so.11 OLD_FILES+=usr/lib32/libkdc_p.a OLD_FILES+=usr/lib32/libkrb5.a OLD_FILES+=usr/lib32/libkrb5.so OLD_LIBS+=usr/lib32/libkrb5.so.11 OLD_FILES+=usr/lib32/libkrb5_p.a OLD_FILES+=usr/lib32/libroken.a OLD_FILES+=usr/lib32/libroken.so OLD_LIBS+=usr/lib32/libroken.so.11 OLD_FILES+=usr/lib32/libroken_p.a OLD_FILES+=usr/lib32/libwind.a OLD_FILES+=usr/lib32/libwind.so OLD_LIBS+=usr/lib32/libwind.so.11 OLD_FILES+=usr/lib32/libwind_p.a OLD_FILES+=usr/lib32/pam_krb5.so OLD_LIBS+=usr/lib32/pam_krb5.so.5 OLD_FILES+=usr/lib32/pam_ksu.so OLD_LIBS+=usr/lib32/pam_ksu.so.5 OLD_FILES+=usr/lib32/private/libheimipcc.a OLD_FILES+=usr/lib32/private/libheimipcc.so OLD_LIBS+=usr/lib32/private/libheimipcc.so.11 OLD_FILES+=usr/lib32/private/libheimipcc_p.a OLD_FILES+=usr/lib32/private/libheimipcs.a OLD_FILES+=usr/lib32/private/libheimipcs.so OLD_LIBS+=usr/lib32/private/libheimipcs.so.11 OLD_FILES+=usr/lib32/private/libheimipcs_p.a .endif OLD_FILES+=usr/libexec/digest-service OLD_FILES+=usr/libexec/hprop OLD_FILES+=usr/libexec/hpropd OLD_FILES+=usr/libexec/ipropd-master OLD_FILES+=usr/libexec/ipropd-slave OLD_FILES+=usr/libexec/kadmind OLD_FILES+=usr/libexec/kcm OLD_FILES+=usr/libexec/kdc OLD_FILES+=usr/libexec/kdigest OLD_FILES+=usr/libexec/kfd OLD_FILES+=usr/libexec/kimpersonate OLD_FILES+=usr/libexec/kpasswdd OLD_FILES+=usr/sbin/kstash OLD_FILES+=usr/sbin/ktutil OLD_FILES+=usr/sbin/iprop-log OLD_FILES+=usr/share/info/heimdal.info.gz OLD_FILES+=usr/share/man/man1/kdestroy.1.gz OLD_FILES+=usr/share/man/man1/kf.1.gz OLD_FILES+=usr/share/man/man1/kinit.1.gz OLD_FILES+=usr/share/man/man1/klist.1.gz OLD_FILES+=usr/share/man/man1/kpasswd.1.gz OLD_FILES+=usr/share/man/man1/krb5-config.1.gz OLD_FILES+=usr/share/man/man1/kswitch.1.gz OLD_FILES+=usr/share/man/man3/HDB.3.gz OLD_FILES+=usr/share/man/man3/hdb__del.3.gz OLD_FILES+=usr/share/man/man3/hdb__get.3.gz OLD_FILES+=usr/share/man/man3/hdb__put.3.gz OLD_FILES+=usr/share/man/man3/hdb_auth_status.3.gz OLD_FILES+=usr/share/man/man3/hdb_check_constrained_delegation.3.gz OLD_FILES+=usr/share/man/man3/hdb_check_pkinit_ms_upn_match.3.gz OLD_FILES+=usr/share/man/man3/hdb_check_s4u2self.3.gz OLD_FILES+=usr/share/man/man3/hdb_close.3.gz OLD_FILES+=usr/share/man/man3/hdb_destroy.3.gz OLD_FILES+=usr/share/man/man3/hdb_entry_ex.3.gz OLD_FILES+=usr/share/man/man3/hdb_fetch_kvno.3.gz OLD_FILES+=usr/share/man/man3/hdb_firstkey.3.gz OLD_FILES+=usr/share/man/man3/hdb_free.3.gz OLD_FILES+=usr/share/man/man3/hdb_get_realms.3.gz OLD_FILES+=usr/share/man/man3/hdb_lock.3.gz OLD_FILES+=usr/share/man/man3/hdb_name.3.gz OLD_FILES+=usr/share/man/man3/hdb_nextkey.3.gz OLD_FILES+=usr/share/man/man3/hdb_open.3.gz OLD_FILES+=usr/share/man/man3/hdb_password.3.gz OLD_FILES+=usr/share/man/man3/hdb_remove.3.gz OLD_FILES+=usr/share/man/man3/hdb_rename.3.gz OLD_FILES+=usr/share/man/man3/hdb_store.3.gz OLD_FILES+=usr/share/man/man3/hdb_unlock.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_build_ntlm1_master.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_build_ntlm2_master.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_calculate_lm2.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_calculate_ntlm1.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_calculate_ntlm2.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_decode_targetinfo.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_encode_targetinfo.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_encode_type1.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_encode_type2.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_encode_type3.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_free_buf.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_free_targetinfo.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_free_type1.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_free_type2.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_free_type3.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_keyex_unwrap.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_nt_key.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_ntlmv2_key.3.gz OLD_FILES+=usr/share/man/man3/heim_ntlm_verify_ntlm2.3.gz OLD_FILES+=usr/share/man/man3/hx509.3.gz OLD_FILES+=usr/share/man/man3/hx509_bitstring_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_sign.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_sign_self.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_crl_dp_uri.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_eku.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_hostname.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_jid.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_ms_upn.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_otherName.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_pkinit.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_add_san_rfc822name.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_ca.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_domaincontroller.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_notAfter.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_notAfter_lifetime.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_notBefore.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_proxy.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_serialnumber.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_spki.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_subject.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_template.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_set_unique.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_subject_expand.3.gz OLD_FILES+=usr/share/man/man3/hx509_ca_tbs_template_units.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_binary.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_check_eku.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_cmp.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_find_subjectAltName_otherName.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_SPKI.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_SPKI_AlgorithmIdentifier.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_attribute.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_base_subject.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_friendly_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_issuer.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_issuer_unique_id.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_notAfter.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_notBefore.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_serialnumber.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_subject.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_get_subject_unique_id.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_init_data.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_keyusage_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_ref.3.gz OLD_FILES+=usr/share/man/man3/hx509_cert_set_friendly_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_add.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_append.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_end_seq.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_filter.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_find.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_info.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_iter_f.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_merge.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_next_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_start_seq.3.gz OLD_FILES+=usr/share/man/man3/hx509_certs_store.3.gz OLD_FILES+=usr/share/man/man3/hx509_ci_print_names.3.gz OLD_FILES+=usr/share/man/man3/hx509_clear_error_string.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_create_signed_1.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_envelope_1.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_unenvelope.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_unwrap_ContentInfo.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_verify_signed.3.gz OLD_FILES+=usr/share/man/man3/hx509_cms_wrap_ContentInfo.3.gz OLD_FILES+=usr/share/man/man3/hx509_context_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_context_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_context_set_missing_revoke.3.gz OLD_FILES+=usr/share/man/man3/hx509_crl_add_revoked_certs.3.gz OLD_FILES+=usr/share/man/man3/hx509_crl_alloc.3.gz OLD_FILES+=usr/share/man/man3/hx509_crl_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_crl_lifetime.3.gz OLD_FILES+=usr/share/man/man3/hx509_crl_sign.3.gz OLD_FILES+=usr/share/man/man3/hx509_crypto.3.gz OLD_FILES+=usr/share/man/man3/hx509_env.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_add.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_add_binding.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_find.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_find_binding.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_env_lfind.3.gz OLD_FILES+=usr/share/man/man3/hx509_err.3.gz OLD_FILES+=usr/share/man/man3/hx509_error.3.gz OLD_FILES+=usr/share/man/man3/hx509_free_error_string.3.gz OLD_FILES+=usr/share/man/man3/hx509_free_octet_string_list.3.gz OLD_FILES+=usr/share/man/man3/hx509_general_name_unparse.3.gz OLD_FILES+=usr/share/man/man3/hx509_get_error_string.3.gz OLD_FILES+=usr/share/man/man3/hx509_get_one_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_keyset.3.gz OLD_FILES+=usr/share/man/man3/hx509_lock.3.gz OLD_FILES+=usr/share/man/man3/hx509_misc.3.gz OLD_FILES+=usr/share/man/man3/hx509_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_binary.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_cmp.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_copy.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_expand.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_is_null_p.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_to_Name.3.gz OLD_FILES+=usr/share/man/man3/hx509_name_to_string.3.gz OLD_FILES+=usr/share/man/man3/hx509_ocsp_request.3.gz OLD_FILES+=usr/share/man/man3/hx509_ocsp_verify.3.gz OLD_FILES+=usr/share/man/man3/hx509_oid_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_oid_sprint.3.gz OLD_FILES+=usr/share/man/man3/hx509_parse_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer_info_add_cms_alg.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer_info_alloc.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer_info_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer_info_set_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_peer_info_set_cms_algs.3.gz OLD_FILES+=usr/share/man/man3/hx509_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_print_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_print_stdout.3.gz OLD_FILES+=usr/share/man/man3/hx509_query.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_alloc.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_match_cmp_func.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_match_eku.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_match_friendly_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_match_issuer_serial.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_match_option.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_statistic_file.3.gz OLD_FILES+=usr/share/man/man3/hx509_query_unparse_stats.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_add_crl.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_add_ocsp.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_ocsp_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_revoke_verify.3.gz OLD_FILES+=usr/share/man/man3/hx509_set_error_string.3.gz OLD_FILES+=usr/share/man/man3/hx509_set_error_stringv.3.gz OLD_FILES+=usr/share/man/man3/hx509_unparse_der_name.3.gz OLD_FILES+=usr/share/man/man3/hx509_validate_cert.3.gz OLD_FILES+=usr/share/man/man3/hx509_validate_ctx_add_flags.3.gz OLD_FILES+=usr/share/man/man3/hx509_validate_ctx_free.3.gz OLD_FILES+=usr/share/man/man3/hx509_validate_ctx_init.3.gz OLD_FILES+=usr/share/man/man3/hx509_validate_ctx_set_print.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_attach_anchors.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_attach_revoke.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_ctx_f_allow_default_trustanchors.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_destroy_ctx.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_hostname.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_init_ctx.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_path.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_set_max_depth.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_set_proxy_certificate.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_set_strict_rfc3280_verification.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_set_time.3.gz OLD_FILES+=usr/share/man/man3/hx509_verify_signature.3.gz OLD_FILES+=usr/share/man/man3/hx509_xfree.3.gz OLD_FILES+=usr/share/man/man3/k_afs_cell_of_file.3.gz OLD_FILES+=usr/share/man/man3/k_hasafs.3.gz OLD_FILES+=usr/share/man/man3/k_pioctl.3.gz OLD_FILES+=usr/share/man/man3/k_setpag.3.gz OLD_FILES+=usr/share/man/man3/k_unlog.3.gz OLD_FILES+=usr/share/man/man3/kadm5_pwcheck.3.gz OLD_FILES+=usr/share/man/man3/kafs.3.gz OLD_FILES+=usr/share/man/man3/kafs5.3.gz OLD_FILES+=usr/share/man/man3/kafs_set_verbose.3.gz OLD_FILES+=usr/share/man/man3/kafs_settoken.3.gz OLD_FILES+=usr/share/man/man3/kafs_settoken5.3.gz OLD_FILES+=usr/share/man/man3/kafs_settoken_rxkad.3.gz OLD_FILES+=usr/share/man/man3/krb5.3.gz OLD_FILES+=usr/share/man/man3/krb524_convert_creds_kdc.3.gz OLD_FILES+=usr/share/man/man3/krb524_convert_creds_kdc_ccache.3.gz OLD_FILES+=usr/share/man/man3/krb5_425_conv_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_425_conv_principal_ext.3.gz OLD_FILES+=usr/share/man/man3/krb5_524_conv_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_acc_ops.3.gz OLD_FILES+=usr/share/man/man3/krb5_acl_match_file.3.gz OLD_FILES+=usr/share/man/man3/krb5_acl_match_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_add_et_list.3.gz OLD_FILES+=usr/share/man/man3/krb5_add_extra_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_add_ignore_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_addlog_dest.3.gz OLD_FILES+=usr/share/man/man3/krb5_addlog_func.3.gz OLD_FILES+=usr/share/man/man3/krb5_addr2sockaddr.3.gz OLD_FILES+=usr/share/man/man3/krb5_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_address_compare.3.gz OLD_FILES+=usr/share/man/man3/krb5_address_order.3.gz OLD_FILES+=usr/share/man/man3/krb5_address_prefixlen_boundary.3.gz OLD_FILES+=usr/share/man/man3/krb5_address_search.3.gz OLD_FILES+=usr/share/man/man3/krb5_afslog.3.gz OLD_FILES+=usr/share/man/man3/krb5_afslog_uid.3.gz OLD_FILES+=usr/share/man/man3/krb5_allow_weak_crypto.3.gz OLD_FILES+=usr/share/man/man3/krb5_aname_to_localname.3.gz OLD_FILES+=usr/share/man/man3/krb5_anyaddr.3.gz OLD_FILES+=usr/share/man/man3/krb5_appdefault.3.gz OLD_FILES+=usr/share/man/man3/krb5_appdefault_boolean.3.gz OLD_FILES+=usr/share/man/man3/krb5_appdefault_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_appdefault_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_append_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_genaddrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getaddrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getflags.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getlocalsubkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getrcache.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getremotesubkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_getuserkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_initivector.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setaddrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setaddrs_from_fd.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setflags.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setivector.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setlocalsubkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setrcache.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setremotesubkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_con_setuserkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_context.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_getauthenticator.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_getcksumtype.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_getkeytype.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_getlocalseqnumber.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_getremoteseqnumber.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_setcksumtype.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_setkeytype.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_setlocalseqnumber.3.gz OLD_FILES+=usr/share/man/man3/krb5_auth_setremoteseqnumber.3.gz OLD_FILES+=usr/share/man/man3/krb5_build_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_build_principal_ext.3.gz OLD_FILES+=usr/share/man/man3/krb5_build_principal_va.3.gz OLD_FILES+=usr/share/man/man3/krb5_build_principal_va_ext.3.gz OLD_FILES+=usr/share/man/man3/krb5_c_enctype_compare.3.gz OLD_FILES+=usr/share/man/man3/krb5_c_make_checksum.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_cache_end_seq_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_cache_get_first.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_cache_match.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_cache_next.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_clear_mcred.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_close.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_copy_cache.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_copy_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_copy_match_f.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_default_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_destroy.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_end_seq_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_gen_new.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_config.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_friendly_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_full_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_kdc_offset.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_lifetime.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_ops.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_prefix_ops.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_type.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_get_version.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_initialize.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_last_change_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_move.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_new_unique.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_next_cred.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_register.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_remove_cred.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_resolve.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_retrieve_cred.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_set_config.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_set_default_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_set_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_set_friendly_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_set_kdc_offset.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_start_seq_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_store_cred.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_support_switch.3.gz OLD_FILES+=usr/share/man/man3/krb5_cc_switch.3.gz OLD_FILES+=usr/share/man/man3/krb5_ccache.3.gz OLD_FILES+=usr/share/man/man3/krb5_ccache_intro.3.gz OLD_FILES+=usr/share/man/man3/krb5_cccol_cursor_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_cccol_cursor_new.3.gz OLD_FILES+=usr/share/man/man3/krb5_cccol_cursor_next.3.gz OLD_FILES+=usr/share/man/man3/krb5_cccol_last_change_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_change_password.3.gz OLD_FILES+=usr/share/man/man3/krb5_check_transited.3.gz OLD_FILES+=usr/share/man/man3/krb5_checksum_is_collision_proof.3.gz OLD_FILES+=usr/share/man/man3/krb5_checksum_is_keyed.3.gz OLD_FILES+=usr/share/man/man3/krb5_checksumsize.3.gz OLD_FILES+=usr/share/man/man3/krb5_cksumtype_to_enctype.3.gz OLD_FILES+=usr/share/man/man3/krb5_clear_error_message.3.gz OLD_FILES+=usr/share/man/man3/krb5_clear_error_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_closelog.3.gz OLD_FILES+=usr/share/man/man3/krb5_compare_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_file_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_free_strings.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_bool.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_bool_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_list.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_string_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_strings.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_get_time_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_parse_file_multi.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_parse_string_multi.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_bool.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_bool_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_list.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_string_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_strings.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_config_vget_time_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_context.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_creds_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_host_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_keyblock.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_keyblock_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_copy_ticket.3.gz OLD_FILES+=usr/share/man/man3/krb5_create_checksum.3.gz OLD_FILES+=usr/share/man/man3/krb5_create_checksum_iov.3.gz OLD_FILES+=usr/share/man/man3/krb5_credential.3.gz OLD_FILES+=usr/share/man/man3/krb5_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_creds_get_ticket_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_destroy.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_fx_cf2.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_getblocksize.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_getconfoundersize.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_getenctype.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_getpadsize.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_crypto_iov.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_alloc.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_cmp.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_copy.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_ct_cmp.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_realloc.3.gz OLD_FILES+=usr/share/man/man3/krb5_data_zero.3.gz OLD_FILES+=usr/share/man/man3/krb5_decrypt.3.gz OLD_FILES+=usr/share/man/man3/krb5_decrypt_EncryptedData.3.gz OLD_FILES+=usr/share/man/man3/krb5_decrypt_iov_ivec.3.gz OLD_FILES+=usr/share/man/man3/krb5_deprecated.3.gz OLD_FILES+=usr/share/man/man3/krb5_digest.3.gz OLD_FILES+=usr/share/man/man3/krb5_digest_probe.3.gz OLD_FILES+=usr/share/man/man3/krb5_eai_to_heim_errno.3.gz OLD_FILES+=usr/share/man/man3/krb5_encrypt.3.gz OLD_FILES+=usr/share/man/man3/krb5_encrypt_EncryptedData.3.gz OLD_FILES+=usr/share/man/man3/krb5_encrypt_iov_ivec.3.gz OLD_FILES+=usr/share/man/man3/krb5_enctype_disable.3.gz OLD_FILES+=usr/share/man/man3/krb5_enctype_enable.3.gz OLD_FILES+=usr/share/man/man3/krb5_enctype_valid.3.gz OLD_FILES+=usr/share/man/man3/krb5_enctypes_compatible_keys.3.gz OLD_FILES+=usr/share/man/man3/krb5_error.3.gz OLD_FILES+=usr/share/man/man3/krb5_expand_hostname.3.gz OLD_FILES+=usr/share/man/man3/krb5_expand_hostname_realms.3.gz OLD_FILES+=usr/share/man/man3/krb5_fcc_ops.3.gz OLD_FILES+=usr/share/man/man3/krb5_fileformats.3.gz OLD_FILES+=usr/share/man/man3/krb5_find_padata.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_config_files.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_context.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_cred_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_creds_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_data_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_error_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_host_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_keyblock.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_keyblock_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_krbhst.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_ticket.3.gz OLD_FILES+=usr/share/man/man3/krb5_free_unparsed_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_fwd_tgt_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_generate_random_block.3.gz OLD_FILES+=usr/share/man/man3/krb5_generate_subkey.3.gz OLD_FILES+=usr/share/man/man3/krb5_generate_subkey_extended.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_all_client_addrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_all_server_addrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_cred_from_kdc.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_cred_from_kdc_opt.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_credentials.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_default_config_files.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_default_in_tkt_etypes.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_default_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_default_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_default_realms.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_dns_canonicalize_hostname.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_extra_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_fcache_version.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_forwarded_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_host_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_ignore_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_in_cred.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_in_tkt_with_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_in_tkt_with_password.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_in_tkt_with_skey.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_keyblock.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_opt_alloc.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_opt_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_opt_get_error.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_opt_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_init_creds_password.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_kdc_sec_offset.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_krb524hst.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_krb_admin_hst.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_krb_changepw_hst.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_krbhst.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_max_time_skew.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_use_admin_kdc.3.gz OLD_FILES+=usr/share/man/man3/krb5_get_validated_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_getportbyname.3.gz OLD_FILES+=usr/share/man/man3/krb5_h_addr2addr.3.gz OLD_FILES+=usr/share/man/man3/krb5_h_addr2sockaddr.3.gz OLD_FILES+=usr/share/man/man3/krb5_h_errno_to_heim_errno.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_context.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_get_error.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_intro.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_set_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_set_password.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_set_service.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_creds_step.3.gz OLD_FILES+=usr/share/man/man3/krb5_init_ets.3.gz OLD_FILES+=usr/share/man/man3/krb5_initlog.3.gz OLD_FILES+=usr/share/man/man3/krb5_introduction.3.gz OLD_FILES+=usr/share/man/man3/krb5_is_config_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_is_thread_safe.3.gz OLD_FILES+=usr/share/man/man3/krb5_kerberos_enctypes.3.gz OLD_FILES+=usr/share/man/man3/krb5_keyblock_get_enctype.3.gz OLD_FILES+=usr/share/man/man3/krb5_keyblock_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_keyblock_zero.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytab_intro.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytab_key_proc.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytype_to_enctypes.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytype_to_enctypes_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_keytype_to_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_format_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_get_addrinfo.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_next.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_next_as_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_krbhst_reset.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_add_entry.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_close.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_compare.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_copy_entry_contents.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_default_modify_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_default_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_destroy.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_end_seq_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_free_entry.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_get_entry.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_get_full_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_get_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_get_type.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_have_content.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_next_entry.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_read_service_key.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_register.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_remove_entry.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_resolve.3.gz OLD_FILES+=usr/share/man/man3/krb5_kt_start_seq_get.3.gz OLD_FILES+=usr/share/man/man3/krb5_kuserok.3.gz OLD_FILES+=usr/share/man/man3/krb5_log.3.gz OLD_FILES+=usr/share/man/man3/krb5_log_msg.3.gz OLD_FILES+=usr/share/man/man3/krb5_make_addrport.3.gz OLD_FILES+=usr/share/man/man3/krb5_make_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_max_sockaddr_size.3.gz OLD_FILES+=usr/share/man/man3/krb5_mcc_ops.3.gz OLD_FILES+=usr/share/man/man3/krb5_mk_req.3.gz OLD_FILES+=usr/share/man/man3/krb5_mk_safe.3.gz OLD_FILES+=usr/share/man/man3/krb5_openlog.3.gz OLD_FILES+=usr/share/man/man3/krb5_pac.3.gz OLD_FILES+=usr/share/man/man3/krb5_pac_get_buffer.3.gz OLD_FILES+=usr/share/man/man3/krb5_pac_verify.3.gz OLD_FILES+=usr/share/man/man3/krb5_parse_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_parse_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_parse_name_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_parse_nametype.3.gz OLD_FILES+=usr/share/man/man3/krb5_password_key_proc.3.gz OLD_FILES+=usr/share/man/man3/krb5_plugin_register.3.gz OLD_FILES+=usr/share/man/man3/krb5_prepend_config_files_default.3.gz OLD_FILES+=usr/share/man/man3/krb5_princ_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_princ_set_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_compare.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_compare_any_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_get_comp_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_get_num_comp.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_get_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_get_type.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_intro.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_is_krbtgt.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_match.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_set_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_principal_set_type.3.gz OLD_FILES+=usr/share/man/man3/krb5_print_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_random_to_key.3.gz OLD_FILES+=usr/share/man/man3/krb5_rcache.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_error.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_ctx.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_in_ctx_alloc.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_in_set_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_in_set_pac_check.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_out_ctx_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_req_out_get_server.3.gz OLD_FILES+=usr/share/man/man3/krb5_rd_safe.3.gz OLD_FILES+=usr/share/man/man3/krb5_realm_compare.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_addrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_authdata.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_creds_tag.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_int16.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_int32.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_int8.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_keyblock.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_stringz.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_times.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_uint16.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_uint32.3.gz OLD_FILES+=usr/share/man/man3/krb5_ret_uint8.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_config_files.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_default_in_tkt_etypes.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_default_realm.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_dns_canonicalize_hostname.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_error_message.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_error_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_extra_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_fcache_version.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_home_dir_access.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_ignore_addresses.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_kdc_sec_offset.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_max_time_skew.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_password.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_real_time.3.gz OLD_FILES+=usr/share/man/man3/krb5_set_use_admin_kdc.3.gz OLD_FILES+=usr/share/man/man3/krb5_sname_to_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_sock_to_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_sockaddr2address.3.gz OLD_FILES+=usr/share/man/man3/krb5_sockaddr2port.3.gz OLD_FILES+=usr/share/man/man3/krb5_sockaddr_uninteresting.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_clear_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_emem.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_free.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_from_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_from_fd.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_from_mem.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_from_readonly_mem.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_get_byteorder.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_get_eof_code.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_is_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_read.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_seek.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_set_byteorder.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_set_eof_code.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_set_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_set_max_alloc.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_to_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_truncate.3.gz OLD_FILES+=usr/share/man/man3/krb5_storage_write.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_address.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_addrs.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_authdata.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_creds_tag.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_data.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_int16.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_int32.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_int8.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_keyblock.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_principal.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_stringz.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_times.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_uint16.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_uint32.3.gz OLD_FILES+=usr/share/man/man3/krb5_store_uint8.3.gz OLD_FILES+=usr/share/man/man3/krb5_string_to_key.3.gz OLD_FILES+=usr/share/man/man3/krb5_string_to_keytype.3.gz OLD_FILES+=usr/share/man/man3/krb5_support.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket_get_authorization_data_type.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket_get_client.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket_get_endtime.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket_get_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_ticket_get_server.3.gz OLD_FILES+=usr/share/man/man3/krb5_timeofday.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name_fixed.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name_fixed_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name_fixed_short.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_unparse_name_short.3.gz OLD_FILES+=usr/share/man/man3/krb5_us_timeofday.3.gz OLD_FILES+=usr/share/man/man3/krb5_v4compat.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_checksum.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_checksum_iov.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_init_creds.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_opt_init.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_opt_set_flags.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_opt_set_keytab.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_opt_set_secure.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_opt_set_service.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_user.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_user_lrealm.3.gz OLD_FILES+=usr/share/man/man3/krb5_verify_user_opt.3.gz OLD_FILES+=usr/share/man/man3/krb5_vlog.3.gz OLD_FILES+=usr/share/man/man3/krb5_vlog_msg.3.gz OLD_FILES+=usr/share/man/man3/krb5_vset_error_string.3.gz OLD_FILES+=usr/share/man/man3/krb5_vwarn.3.gz OLD_FILES+=usr/share/man/man3/krb_afslog.3.gz OLD_FILES+=usr/share/man/man3/krb_afslog_uid.3.gz OLD_FILES+=usr/share/man/man3/ntlm_buf.3.gz OLD_FILES+=usr/share/man/man3/ntlm_core.3.gz OLD_FILES+=usr/share/man/man3/ntlm_type1.3.gz OLD_FILES+=usr/share/man/man3/ntlm_type2.3.gz OLD_FILES+=usr/share/man/man3/ntlm_type3.3.gz OLD_FILES+=usr/share/man/man5/krb5.conf.5.gz OLD_FILES+=usr/share/man/man8/hprop.8.gz OLD_FILES+=usr/share/man/man8/hpropd.8.gz OLD_FILES+=usr/share/man/man8/iprop-log.8.gz OLD_FILES+=usr/share/man/man8/iprop.8.gz OLD_FILES+=usr/share/man/man8/kadmin.8.gz OLD_FILES+=usr/share/man/man8/kadmind.8.gz OLD_FILES+=usr/share/man/man8/kcm.8.gz OLD_FILES+=usr/share/man/man8/kdc.8.gz OLD_FILES+=usr/share/man/man8/kdigest.8.gz OLD_FILES+=usr/share/man/man8/kerberos.8.gz OLD_FILES+=usr/share/man/man8/kimpersonate.8.gz OLD_FILES+=usr/share/man/man8/kpasswdd.8.gz OLD_FILES+=usr/share/man/man8/kstash.8.gz OLD_FILES+=usr/share/man/man8/ktutil.8.gz OLD_FILES+=usr/share/man/man8/pam_krb5.8.gz OLD_FILES+=usr/share/man/man8/pam_ksu.8.gz OLD_FILES+=usr/share/man/man8/string2key.8.gz OLD_FILES+=usr/share/man/man8/verify_krb5_conf.8.gz .endif .if ${MK_LDNS} == no OLD_FILES+=usr/lib/private/libldns.a OLD_FILES+=usr/lib/private/libldns.so OLD_LIBS+=usr/lib/private/libldns.so.5 OLD_FILES+=usr/lib/private/libldns_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/private/libldns.a OLD_FILES+=usr/lib32/private/libldns.so OLD_LIBS+=usr/lib32/private/libldns.so.5 OLD_FILES+=usr/lib32/private/libldns_p.a .endif .endif .if ${MK_LDNS_UTILS} == no OLD_FILES+=usr/bin/drill OLD_FILES+=usr/share/man/man1/drill.1.gz OLD_FILES+=usr/bin/host OLD_FILES+=usr/share/man/man1/host.1.gz .endif .if ${MK_LEGACY_CONSOLE} == no OLD_FILES+=etc/rc.d/moused OLD_FILES+=etc/rc.d/syscons OLD_FILES+=usr/sbin/kbdcontrol OLD_FILES+=usr/sbin/kbdmap OLD_FILES+=usr/sbin/moused OLD_FILES+=usr/sbin/vidcontrol OLD_FILES+=usr/sbin/vidfont OLD_FILES+=usr/share/man/man1/kbdcontrol.1.gz OLD_FILES+=usr/share/man/man1/kbdmap.1.gz OLD_FILES+=usr/share/man/man1/vidcontrol.1.gz OLD_FILES+=usr/share/man/man1/vidfont.1.gz OLD_FILES+=usr/share/man/man5/kbdmap.5.gz OLD_FILES+=usr/share/man/man5/keymap.5.gz OLD_FILES+=usr/share/man/man8/moused.8.g .endif .if ${MK_LIB32} == no OLD_FILES+=etc/libmap32.conf .endif .if ${MK_LIBCPLUSPLUS} == no OLD_LIBS+=lib/libcxxrt.so.1 OLD_FILES+=usr/lib/libc++.a OLD_FILES+=usr/lib/libc++_p.a OLD_FILES+=usr/lib/libc++.so OLD_LIBS+=usr/lib/libc++.so.1 OLD_FILES+=usr/lib/libcxxrt.a OLD_FILES+=usr/lib/libcxxrt.so OLD_FILES+=usr/lib/libcxxrt_p.a OLD_FILES+=usr/include/c++/v1/__bit_reference OLD_FILES+=usr/include/c++/v1/__config OLD_FILES+=usr/include/c++/v1/__debug OLD_FILES+=usr/include/c++/v1/__functional_03 OLD_FILES+=usr/include/c++/v1/__functional_base OLD_FILES+=usr/include/c++/v1/__functional_base_03 OLD_FILES+=usr/include/c++/v1/__hash_table OLD_FILES+=usr/include/c++/v1/__locale OLD_FILES+=usr/include/c++/v1/__mutex_base +OLD_FILES+=usr/include/c++/v1/__refstring OLD_FILES+=usr/include/c++/v1/__split_buffer OLD_FILES+=usr/include/c++/v1/__sso_allocator OLD_FILES+=usr/include/c++/v1/__std_stream OLD_FILES+=usr/include/c++/v1/__tree OLD_FILES+=usr/include/c++/v1/__tuple OLD_FILES+=usr/include/c++/v1/__tuple_03 OLD_FILES+=usr/include/c++/v1/__undef_min_max OLD_FILES+=usr/include/c++/v1/algorithm OLD_FILES+=usr/include/c++/v1/array OLD_FILES+=usr/include/c++/v1/atomic OLD_FILES+=usr/include/c++/v1/bitset OLD_FILES+=usr/include/c++/v1/cassert OLD_FILES+=usr/include/c++/v1/ccomplex OLD_FILES+=usr/include/c++/v1/cctype OLD_FILES+=usr/include/c++/v1/cerrno OLD_FILES+=usr/include/c++/v1/cfenv OLD_FILES+=usr/include/c++/v1/cfloat OLD_FILES+=usr/include/c++/v1/chrono OLD_FILES+=usr/include/c++/v1/cinttypes OLD_FILES+=usr/include/c++/v1/ciso646 OLD_FILES+=usr/include/c++/v1/climits OLD_FILES+=usr/include/c++/v1/clocale OLD_FILES+=usr/include/c++/v1/cmath OLD_FILES+=usr/include/c++/v1/codecvt OLD_FILES+=usr/include/c++/v1/complex OLD_FILES+=usr/include/c++/v1/complex.h OLD_FILES+=usr/include/c++/v1/condition_variable OLD_FILES+=usr/include/c++/v1/csetjmp OLD_FILES+=usr/include/c++/v1/csignal OLD_FILES+=usr/include/c++/v1/cstdarg OLD_FILES+=usr/include/c++/v1/cstdbool OLD_FILES+=usr/include/c++/v1/cstddef OLD_FILES+=usr/include/c++/v1/cstdint OLD_FILES+=usr/include/c++/v1/cstdio OLD_FILES+=usr/include/c++/v1/cstdlib OLD_FILES+=usr/include/c++/v1/cstring OLD_FILES+=usr/include/c++/v1/ctgmath OLD_FILES+=usr/include/c++/v1/ctime OLD_FILES+=usr/include/c++/v1/cwchar OLD_FILES+=usr/include/c++/v1/cwctype OLD_FILES+=usr/include/c++/v1/cxxabi.h OLD_FILES+=usr/include/c++/v1/deque OLD_FILES+=usr/include/c++/v1/exception +OLD_FILES+=usr/include/c++/v1/experimental/__config OLD_FILES+=usr/include/c++/v1/experimental/dynarray OLD_FILES+=usr/include/c++/v1/experimental/optional +OLD_FILES+=usr/include/c++/v1/experimental/string_view +OLD_FILES+=usr/include/c++/v1/experimental/type_traits +OLD_FILES+=usr/include/c++/v1/experimental/utility OLD_FILES+=usr/include/c++/v1/ext/__hash OLD_FILES+=usr/include/c++/v1/ext/hash_map OLD_FILES+=usr/include/c++/v1/ext/hash_set OLD_FILES+=usr/include/c++/v1/forward_list OLD_FILES+=usr/include/c++/v1/fstream OLD_FILES+=usr/include/c++/v1/functional OLD_FILES+=usr/include/c++/v1/future OLD_FILES+=usr/include/c++/v1/initializer_list OLD_FILES+=usr/include/c++/v1/iomanip OLD_FILES+=usr/include/c++/v1/ios OLD_FILES+=usr/include/c++/v1/iosfwd OLD_FILES+=usr/include/c++/v1/iostream OLD_FILES+=usr/include/c++/v1/istream OLD_FILES+=usr/include/c++/v1/iterator OLD_FILES+=usr/include/c++/v1/limits OLD_FILES+=usr/include/c++/v1/list OLD_FILES+=usr/include/c++/v1/locale OLD_FILES+=usr/include/c++/v1/map OLD_FILES+=usr/include/c++/v1/memory OLD_FILES+=usr/include/c++/v1/mutex OLD_FILES+=usr/include/c++/v1/new OLD_FILES+=usr/include/c++/v1/numeric OLD_FILES+=usr/include/c++/v1/ostream OLD_FILES+=usr/include/c++/v1/queue OLD_FILES+=usr/include/c++/v1/random OLD_FILES+=usr/include/c++/v1/ratio OLD_FILES+=usr/include/c++/v1/regex OLD_FILES+=usr/include/c++/v1/scoped_allocator OLD_FILES+=usr/include/c++/v1/set +OLD_FILES+=usr/include/c++/v1/shared_mutex OLD_FILES+=usr/include/c++/v1/sstream OLD_FILES+=usr/include/c++/v1/stack OLD_FILES+=usr/include/c++/v1/stdexcept OLD_FILES+=usr/include/c++/v1/streambuf OLD_FILES+=usr/include/c++/v1/string OLD_FILES+=usr/include/c++/v1/strstream OLD_FILES+=usr/include/c++/v1/system_error OLD_FILES+=usr/include/c++/v1/tgmath.h OLD_FILES+=usr/include/c++/v1/thread +OLD_FILES+=usr/include/c++/v1/tr1/__bit_reference +OLD_FILES+=usr/include/c++/v1/tr1/__config +OLD_FILES+=usr/include/c++/v1/tr1/__debug +OLD_FILES+=usr/include/c++/v1/tr1/__functional_03 +OLD_FILES+=usr/include/c++/v1/tr1/__functional_base +OLD_FILES+=usr/include/c++/v1/tr1/__functional_base_03 +OLD_FILES+=usr/include/c++/v1/tr1/__hash_table +OLD_FILES+=usr/include/c++/v1/tr1/__locale +OLD_FILES+=usr/include/c++/v1/tr1/__mutex_base +OLD_FILES+=usr/include/c++/v1/tr1/__refstring +OLD_FILES+=usr/include/c++/v1/tr1/__split_buffer +OLD_FILES+=usr/include/c++/v1/tr1/__sso_allocator +OLD_FILES+=usr/include/c++/v1/tr1/__std_stream +OLD_FILES+=usr/include/c++/v1/tr1/__tree +OLD_FILES+=usr/include/c++/v1/tr1/__tuple +OLD_FILES+=usr/include/c++/v1/tr1/__tuple_03 +OLD_FILES+=usr/include/c++/v1/tr1/__undef_min_max +OLD_FILES+=usr/include/c++/v1/tr1/algorithm +OLD_FILES+=usr/include/c++/v1/tr1/array +OLD_FILES+=usr/include/c++/v1/tr1/atomic +OLD_FILES+=usr/include/c++/v1/tr1/bitset +OLD_FILES+=usr/include/c++/v1/tr1/cassert +OLD_FILES+=usr/include/c++/v1/tr1/ccomplex +OLD_FILES+=usr/include/c++/v1/tr1/cctype +OLD_FILES+=usr/include/c++/v1/tr1/cerrno +OLD_FILES+=usr/include/c++/v1/tr1/cfenv +OLD_FILES+=usr/include/c++/v1/tr1/cfloat +OLD_FILES+=usr/include/c++/v1/tr1/chrono +OLD_FILES+=usr/include/c++/v1/tr1/cinttypes +OLD_FILES+=usr/include/c++/v1/tr1/ciso646 +OLD_FILES+=usr/include/c++/v1/tr1/climits +OLD_FILES+=usr/include/c++/v1/tr1/clocale +OLD_FILES+=usr/include/c++/v1/tr1/cmath +OLD_FILES+=usr/include/c++/v1/tr1/codecvt +OLD_FILES+=usr/include/c++/v1/tr1/complex +OLD_FILES+=usr/include/c++/v1/tr1/complex.h +OLD_FILES+=usr/include/c++/v1/tr1/condition_variable +OLD_FILES+=usr/include/c++/v1/tr1/csetjmp +OLD_FILES+=usr/include/c++/v1/tr1/csignal +OLD_FILES+=usr/include/c++/v1/tr1/cstdarg +OLD_FILES+=usr/include/c++/v1/tr1/cstdbool +OLD_FILES+=usr/include/c++/v1/tr1/cstddef +OLD_FILES+=usr/include/c++/v1/tr1/cstdint +OLD_FILES+=usr/include/c++/v1/tr1/cstdio +OLD_FILES+=usr/include/c++/v1/tr1/cstdlib +OLD_FILES+=usr/include/c++/v1/tr1/cstring +OLD_FILES+=usr/include/c++/v1/tr1/ctgmath +OLD_FILES+=usr/include/c++/v1/tr1/ctime +OLD_FILES+=usr/include/c++/v1/tr1/cwchar +OLD_FILES+=usr/include/c++/v1/tr1/cwctype +OLD_FILES+=usr/include/c++/v1/tr1/deque +OLD_FILES+=usr/include/c++/v1/tr1/exception +OLD_FILES+=usr/include/c++/v1/tr1/forward_list +OLD_FILES+=usr/include/c++/v1/tr1/fstream +OLD_FILES+=usr/include/c++/v1/tr1/functional +OLD_FILES+=usr/include/c++/v1/tr1/future +OLD_FILES+=usr/include/c++/v1/tr1/initializer_list +OLD_FILES+=usr/include/c++/v1/tr1/iomanip +OLD_FILES+=usr/include/c++/v1/tr1/ios +OLD_FILES+=usr/include/c++/v1/tr1/iosfwd +OLD_FILES+=usr/include/c++/v1/tr1/iostream +OLD_FILES+=usr/include/c++/v1/tr1/istream +OLD_FILES+=usr/include/c++/v1/tr1/iterator +OLD_FILES+=usr/include/c++/v1/tr1/limits +OLD_FILES+=usr/include/c++/v1/tr1/list +OLD_FILES+=usr/include/c++/v1/tr1/locale +OLD_FILES+=usr/include/c++/v1/tr1/map +OLD_FILES+=usr/include/c++/v1/tr1/memory +OLD_FILES+=usr/include/c++/v1/tr1/mutex +OLD_FILES+=usr/include/c++/v1/tr1/new +OLD_FILES+=usr/include/c++/v1/tr1/numeric +OLD_FILES+=usr/include/c++/v1/tr1/ostream +OLD_FILES+=usr/include/c++/v1/tr1/queue +OLD_FILES+=usr/include/c++/v1/tr1/random +OLD_FILES+=usr/include/c++/v1/tr1/ratio +OLD_FILES+=usr/include/c++/v1/tr1/regex +OLD_FILES+=usr/include/c++/v1/tr1/scoped_allocator +OLD_FILES+=usr/include/c++/v1/tr1/set +OLD_FILES+=usr/include/c++/v1/tr1/shared_mutex +OLD_FILES+=usr/include/c++/v1/tr1/sstream +OLD_FILES+=usr/include/c++/v1/tr1/stack +OLD_FILES+=usr/include/c++/v1/tr1/stdexcept +OLD_FILES+=usr/include/c++/v1/tr1/streambuf +OLD_FILES+=usr/include/c++/v1/tr1/string +OLD_FILES+=usr/include/c++/v1/tr1/strstream +OLD_FILES+=usr/include/c++/v1/tr1/system_error +OLD_FILES+=usr/include/c++/v1/tr1/tgmath.h +OLD_FILES+=usr/include/c++/v1/tr1/thread +OLD_FILES+=usr/include/c++/v1/tr1/tuple +OLD_FILES+=usr/include/c++/v1/tr1/type_traits +OLD_FILES+=usr/include/c++/v1/tr1/typeindex +OLD_FILES+=usr/include/c++/v1/tr1/typeinfo +OLD_FILES+=usr/include/c++/v1/tr1/unordered_map +OLD_FILES+=usr/include/c++/v1/tr1/unordered_set +OLD_FILES+=usr/include/c++/v1/tr1/utility +OLD_FILES+=usr/include/c++/v1/tr1/valarray +OLD_FILES+=usr/include/c++/v1/tr1/vector OLD_FILES+=usr/include/c++/v1/tuple OLD_FILES+=usr/include/c++/v1/type_traits OLD_FILES+=usr/include/c++/v1/typeindex OLD_FILES+=usr/include/c++/v1/typeinfo OLD_FILES+=usr/include/c++/v1/unordered_map OLD_FILES+=usr/include/c++/v1/unordered_set OLD_FILES+=usr/include/c++/v1/unwind-arm.h OLD_FILES+=usr/include/c++/v1/unwind-itanium.h OLD_FILES+=usr/include/c++/v1/unwind.h OLD_FILES+=usr/include/c++/v1/utility OLD_FILES+=usr/include/c++/v1/valarray OLD_FILES+=usr/include/c++/v1/vector +OLD_FILES+=usr/lib32/libc++.a +OLD_FILES+=usr/lib32/libc++.so +OLD_LIBS+=usr/lib32/libc++.so.1 +OLD_FILES+=usr/lib32/libc++_p.a +OLD_FILES+=usr/lib32/libcxxrt.a +OLD_FILES+=usr/lib32/libcxxrt.so +OLD_LIBS+=usr/lib32/libcxxrt.so.1 +OLD_FILES+=usr/lib32/libcxxrt_p.a OLD_DIRS+=usr/include/c++/v1/experimental OLD_DIRS+=usr/include/c++/v1/ext OLD_DIRS+=usr/include/c++/v1 .endif #.if ${MK_LIBTHR} == no # to be filled in #.endif -#.if ${MK_LOCALES} == no -# to be filled in -#.endif +.if ${MK_LOCALES} == no +OLD_FILES+=usr/share/locale/UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/af_ZA.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/af_ZA.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/am_ET.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_COLLATE +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_CTYPE +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_MESSAGES +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_MONETARY +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_NUMERIC +OLD_FILES+=usr/share/locale/be_BY.CP1131/LC_TIME +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_COLLATE +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_CTYPE +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_MESSAGES +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_MONETARY +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_NUMERIC +OLD_FILES+=usr/share/locale/be_BY.CP1251/LC_TIME +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_COLLATE +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_CTYPE +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_MESSAGES +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_MONETARY +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_NUMERIC +OLD_FILES+=usr/share/locale/be_BY.ISO8859-5/LC_TIME +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/be_BY.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_COLLATE +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_CTYPE +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_MESSAGES +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_MONETARY +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_NUMERIC +OLD_FILES+=usr/share/locale/bg_BG.CP1251/LC_TIME +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/bg_BG.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_AD.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_AD.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_ES.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_ES.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_FR.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_FR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_IT.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ca_IT.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/cs_CZ.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/cs_CZ.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/da_DK.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/da_DK.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/da_DK.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_AT.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_AT.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_AT.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_CH.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_CH.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_CH.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_DE.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_DE.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/de_DE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_COLLATE +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_CTYPE +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_MESSAGES +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_MONETARY +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_NUMERIC +OLD_FILES+=usr/share/locale/el_GR.ISO8859-7/LC_TIME +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/el_GR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_AU.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_AU.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_MONETARY +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_AU.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_AU.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_CA.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_CA.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_MONETARY +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_CA.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_CA.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_GB.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_GB.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_MONETARY +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_GB.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_GB.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_IE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_NZ.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_MONETARY +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_NZ.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_NZ.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_US.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_US.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_MONETARY +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_US.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/en_US.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/es_ES.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/es_ES.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/es_ES.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/et_EE.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/et_EE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/eu_ES.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/eu_ES.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/fi_FI.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/fi_FI.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_BE.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_BE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CA.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CA.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CH.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_CH.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_FR.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/fr_FR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/he_IL.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_COLLATE +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_CTYPE +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_MESSAGES +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_MONETARY +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_NUMERIC +OLD_FILES+=usr/share/locale/hi_IN.ISCII-DEV/LC_TIME +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/hr_HR.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/hr_HR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/hu_HU.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/hu_HU.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_COLLATE +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_CTYPE +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_MONETARY +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/hy_AM.ARMSCII-8/LC_TIME +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/hy_AM.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/is_IS.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/is_IS.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/is_IS.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_CH.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_CH.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_CH.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_IT.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_IT.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/it_IT.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_COLLATE +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_CTYPE +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_MESSAGES +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_MONETARY +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_NUMERIC +OLD_FILES+=usr/share/locale/ja_JP.SJIS/LC_TIME +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ja_JP.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_COLLATE +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_CTYPE +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_MESSAGES +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_MONETARY +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_NUMERIC +OLD_FILES+=usr/share/locale/ja_JP.eucJP/LC_TIME +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_COLLATE +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_CTYPE +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_MESSAGES +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_MONETARY +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_NUMERIC +OLD_FILES+=usr/share/locale/kk_KZ.PT154/LC_TIME +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/kk_KZ.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_COLLATE +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_CTYPE +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_MESSAGES +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_MONETARY +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_NUMERIC +OLD_FILES+=usr/share/locale/ko_KR.CP949/LC_TIME +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ko_KR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_COLLATE +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_CTYPE +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_MESSAGES +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_MONETARY +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_NUMERIC +OLD_FILES+=usr/share/locale/ko_KR.eucKR/LC_TIME +OLD_FILES+=usr/share/locale/la_LN.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/la_LN.ISO8859-13/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-13/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/la_LN.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/la_LN.ISO8859-4/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-4/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.ISO8859-4/LC_TIME +OLD_FILES+=usr/share/locale/la_LN.US-ASCII/LC_COLLATE +OLD_FILES+=usr/share/locale/la_LN.US-ASCII/LC_CTYPE +OLD_FILES+=usr/share/locale/la_LN.US-ASCII/LC_TIME +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_COLLATE +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_CTYPE +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_MESSAGES +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_MONETARY +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_NUMERIC +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-13/LC_TIME +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_COLLATE +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_CTYPE +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_MESSAGES +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_MONETARY +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_NUMERIC +OLD_FILES+=usr/share/locale/lt_LT.ISO8859-4/LC_TIME +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/lt_LT.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_COLLATE +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_CTYPE +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_MESSAGES +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_MONETARY +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_NUMERIC +OLD_FILES+=usr/share/locale/lv_LV.ISO8859-13/LC_TIME +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/lv_LV.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/mn_MN.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/nb_NO.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/nb_NO.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_BE.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_BE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_NL.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/nl_NL.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/nn_NO.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/nn_NO.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/no_NO.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/no_NO.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/no_NO.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/pl_PL.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/pl_PL.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/pt_BR.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/pt_BR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/pt_PT.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/pt_PT.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/ro_RO.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ro_RO.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_COLLATE +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_CTYPE +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_MESSAGES +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_MONETARY +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_NUMERIC +OLD_FILES+=usr/share/locale/ru_RU.CP1251/LC_TIME +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_COLLATE +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_CTYPE +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_MESSAGES +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_MONETARY +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_NUMERIC +OLD_FILES+=usr/share/locale/ru_RU.CP866/LC_TIME +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_COLLATE +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_CTYPE +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_MESSAGES +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_MONETARY +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_NUMERIC +OLD_FILES+=usr/share/locale/ru_RU.ISO8859-5/LC_TIME +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_COLLATE +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_CTYPE +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_MESSAGES +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_MONETARY +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_NUMERIC +OLD_FILES+=usr/share/locale/ru_RU.KOI8-R/LC_TIME +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/ru_RU.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/sk_SK.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/sk_SK.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/sl_SI.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/sl_SI.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_COLLATE +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_CTYPE +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_MESSAGES +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_MONETARY +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_NUMERIC +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-2/LC_TIME +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_COLLATE +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_CTYPE +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_MESSAGES +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_MONETARY +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_NUMERIC +OLD_FILES+=usr/share/locale/sr_YU.ISO8859-5/LC_TIME +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/sr_YU.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_COLLATE +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_CTYPE +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_MESSAGES +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_MONETARY +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_NUMERIC +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-1/LC_TIME +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_COLLATE +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_CTYPE +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_MESSAGES +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_MONETARY +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_NUMERIC +OLD_FILES+=usr/share/locale/sv_SE.ISO8859-15/LC_TIME +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/sv_SE.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_COLLATE +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_CTYPE +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_MESSAGES +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_MONETARY +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_NUMERIC +OLD_FILES+=usr/share/locale/tr_TR.ISO8859-9/LC_TIME +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/tr_TR.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_COLLATE +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_CTYPE +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_MESSAGES +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_MONETARY +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_NUMERIC +OLD_FILES+=usr/share/locale/uk_UA.CP1251/LC_TIME +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_COLLATE +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_CTYPE +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_MESSAGES +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_MONETARY +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_NUMERIC +OLD_FILES+=usr/share/locale/uk_UA.ISO8859-5/LC_TIME +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_COLLATE +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_CTYPE +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_MESSAGES +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_MONETARY +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_NUMERIC +OLD_FILES+=usr/share/locale/uk_UA.KOI8-U/LC_TIME +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/uk_UA.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_CN.GB18030/LC_TIME +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_CN.GB2312/LC_TIME +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_CN.GBK/LC_TIME +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_CN.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_CN.eucCN/LC_TIME +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_HK.Big5HKSCS/LC_TIME +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_HK.UTF-8/LC_TIME +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_TW.Big5/LC_TIME +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_COLLATE +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_CTYPE +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_MESSAGES +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_MONETARY +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_NUMERIC +OLD_FILES+=usr/share/locale/zh_TW.UTF-8/LC_TIME +OLD_FILES+=usr/tests/lib/libc/locale/Kyuafile +OLD_FILES+=usr/tests/lib/libc/locale/io_test +OLD_FILES+=usr/tests/lib/libc/locale/mbrtowc_test +OLD_FILES+=usr/tests/lib/libc/locale/mbsnrtowcs_test +OLD_FILES+=usr/tests/lib/libc/locale/mbstowcs_test +OLD_FILES+=usr/tests/lib/libc/locale/mbtowc_test +OLD_FILES+=usr/tests/lib/libc/locale/wcscspn_test +OLD_FILES+=usr/tests/lib/libc/locale/wcspbrk_test +OLD_FILES+=usr/tests/lib/libc/locale/wcsspn_test +OLD_FILES+=usr/tests/lib/libc/locale/wcstod_test +OLD_FILES+=usr/tests/lib/libc/locale/wctomb_test +.endif .if ${MK_LOCATE} == no OLD_FILES+=etc/locate.rc OLD_FILES+=etc/periodic/weekly/310.locate OLD_FILES+=usr/bin/locate OLD_FILES+=usr/libexec/locate.bigram OLD_FILES+=usr/libexec/locate.code OLD_FILES+=usr/libexec/locate.concatdb OLD_FILES+=usr/libexec/locate.mklocatedb OLD_FILES+=usr/libexec/locate.updatedb OLD_FILES+=usr/share/man/man1/locate.1.gz OLD_FILES+=usr/share/man/man8/locate.updatedb.8.gz OLD_FILES+=usr/share/man/man8/updatedb.8.gz .endif .if ${MK_LPR} == no OLD_FILES+=etc/hosts.lpd OLD_FILES+=etc/printcap OLD_FILES+=etc/rc.d/lpd OLD_FILES+=usr/bin/lp OLD_FILES+=usr/bin/lpq OLD_FILES+=usr/bin/lpr OLD_FILES+=usr/bin/lprm OLD_FILES+=usr/libexec/lpr/ru/bjc-240.sh.sample OLD_FILES+=usr/libexec/lpr/ru/koi2alt OLD_FILES+=usr/libexec/lpr/ru/koi2855 OLD_DIRS+=usr/libexec/lpr/ru OLD_FILES+=usr/libexec/lpr/lpf OLD_DIRS+=usr/libexec/lpr OLD_FILES+=usr/sbin/chkprintcap OLD_FILES+=usr/sbin/lpc OLD_FILES+=usr/sbin/lpd OLD_FILES+=usr/sbin/lptest OLD_FILES+=usr/sbin/pac OLD_FILES+=usr/share/doc/smm/07.lpd/paper.ascii.gz OLD_DIRS+=usr/share/doc/smm/07.lpd OLD_FILES+=usr/share/examples/etc/hosts.lpd OLD_FILES+=usr/share/examples/etc/printcap OLD_FILES+=usr/share/man/man1/lp.1.gz OLD_FILES+=usr/share/man/man1/lpq.1.gz OLD_FILES+=usr/share/man/man1/lpr.1.gz OLD_FILES+=usr/share/man/man1/lprm.1.gz OLD_FILES+=usr/share/man/man1/lptest.1.gz OLD_FILES+=usr/share/man/man5/printcap.5.gz OLD_FILES+=usr/share/man/man8/chkprintcap.8.gz OLD_FILES+=usr/share/man/man8/lpc.8.gz OLD_FILES+=usr/share/man/man8/lpd.8.gz OLD_FILES+=usr/share/man/man8/pac.8.gz .endif .if ${MK_MAC_SUPPORT} == no OLD_FILES+=etc/rc.d/ugidfw OLD_FILES+=usr/include/ugidfw.h OLD_FILES+=usr/lib/libugidfw.a OLD_LIBS+=usr/lib/libugidfw.so.4 OLD_FILES+=usr/lib/libugidfw_p.a OLD_FILES+=usr/sbin/getfmac OLD_FILES+=usr/sbin/getpmac OLD_FILES+=usr/sbin/setfmac OLD_FILES+=usr/sbin/setfsmac OLD_FILES+=usr/sbin/setpmac OLD_FILES+=usr/sbin/ugidfw OLD_FILES+=usr/share/man/man3/bsde_add_rule.3.gz OLD_FILES+=usr/share/man/man3/bsde_delete_rule.3.gz OLD_FILES+=usr/share/man/man3/bsde_get_rule.3.gz OLD_FILES+=usr/share/man/man3/bsde_get_rule_count.3.gz OLD_FILES+=usr/share/man/man3/bsde_get_rule_slots.3.gz OLD_FILES+=usr/share/man/man3/bsde_parse_rule.3.gz OLD_FILES+=usr/share/man/man3/bsde_parse_rule_string.3.gz OLD_FILES+=usr/share/man/man3/bsde_rule_to_string.3.gz OLD_FILES+=usr/share/man/man3/bsde_set_rule.3.gz OLD_FILES+=usr/share/man/man3/libugidfw.3.gz OLD_FILES+=usr/share/man/man8/getfmac.8.gz OLD_FILES+=usr/share/man/man8/getpmac.8.gz OLD_FILES+=usr/share/man/man8/setfmac.8.gz OLD_FILES+=usr/share/man/man8/setfsmac.8.gz OLD_FILES+=usr/share/man/man8/setpmac.8.gz OLD_FILES+=usr/share/man/man8/ugidfw.8.gz .endif .if ${MK_MAIL} == no -OLD_FILES+=etc/pam.d/imap -OLD_FILES+=etc/pam.d/pop3 +OLD_FILES+=etc/aliases +OLD_FILES+=etc/mail.rc +OLD_FILES+=etc/mail/aliases +OLD_FILES+=etc/mail/mailer.conf OLD_FILES+=etc/periodic/daily/130.clean-msgs OLD_FILES+=usr/bin/Mail OLD_FILES+=usr/bin/biff OLD_FILES+=usr/bin/from OLD_FILES+=usr/bin/mail OLD_FILES+=usr/bin/mailx OLD_FILES+=usr/bin/msgs OLD_FILES+=usr/libexec/comsat OLD_FILES+=usr/share/examples/etc/mail.rc OLD_FILES+=usr/share/man/man1/Mail.1.gz OLD_FILES+=usr/share/man/man1/biff.1.gz OLD_FILES+=usr/share/man/man1/from.1.gz OLD_FILES+=usr/share/man/man1/mail.1.gz OLD_FILES+=usr/share/man/man1/mailx.1.gz OLD_FILES+=usr/share/man/man1/msgs.1.gz OLD_FILES+=usr/share/man/man8/comsat.8.gz OLD_FILES+=usr/share/misc/mail.help OLD_FILES+=usr/share/misc/mail.tildehelp .endif .if ${MK_MAILWRAPPER} == no OLD_FILES+=etc/mail/mailer.conf .if ${MK_SENDMAIL} == no OLD_FILES+=usr/sbin/mailwrapper .endif OLD_FILES+=usr/share/man/man8/mailwrapper.8.gz .endif -#.if ${MK_MAN} == no -# This should add a dependency to a special target which removes all man pages. -# Listing all of them here is overkill. -#.endif +.if ${MK_MAKE} == no +OLD_FILES+=usr/bin/make +OLD_FILES+=usr/share/man/man1/make.1.gz +OLD_FILES+=usr/share/mk/atf.test.mk +OLD_FILES+=usr/share/mk/bsd.README +OLD_FILES+=usr/share/mk/bsd.arch.inc.mk +OLD_FILES+=usr/share/mk/bsd.compiler.mk +OLD_FILES+=usr/share/mk/bsd.cpu.mk +OLD_FILES+=usr/share/mk/bsd.crunchgen.mk +OLD_FILES+=usr/share/mk/bsd.dep.mk +OLD_FILES+=usr/share/mk/bsd.doc.mk +OLD_FILES+=usr/share/mk/bsd.dtb.mk +OLD_FILES+=usr/share/mk/bsd.endian.mk +OLD_FILES+=usr/share/mk/bsd.files.mk +OLD_FILES+=usr/share/mk/bsd.incs.mk +OLD_FILES+=usr/share/mk/bsd.info.mk +OLD_FILES+=usr/share/mk/bsd.init.mk +OLD_FILES+=usr/share/mk/bsd.kmod.mk +OLD_FILES+=usr/share/mk/bsd.lib.mk +OLD_FILES+=usr/share/mk/bsd.libnames.mk +OLD_FILES+=usr/share/mk/bsd.links.mk +OLD_FILES+=usr/share/mk/bsd.man.mk +OLD_FILES+=usr/share/mk/bsd.mkopt.mk +OLD_FILES+=usr/share/mk/bsd.nls.mk +OLD_FILES+=usr/share/mk/bsd.obj.mk +OLD_FILES+=usr/share/mk/bsd.opts.mk +OLD_FILES+=usr/share/mk/bsd.own.mk +OLD_FILES+=usr/share/mk/bsd.port.mk +OLD_FILES+=usr/share/mk/bsd.port.options.mk +OLD_FILES+=usr/share/mk/bsd.port.post.mk +OLD_FILES+=usr/share/mk/bsd.port.pre.mk +OLD_FILES+=usr/share/mk/bsd.port.subdir.mk +OLD_FILES+=usr/share/mk/bsd.prog.mk +OLD_FILES+=usr/share/mk/bsd.progs.mk +OLD_FILES+=usr/share/mk/bsd.snmpmod.mk +OLD_FILES+=usr/share/mk/bsd.subdir.mk +OLD_FILES+=usr/share/mk/bsd.symver.mk +OLD_FILES+=usr/share/mk/bsd.sys.mk +OLD_FILES+=usr/share/mk/bsd.test.mk +OLD_FILES+=usr/share/mk/plain.test.mk +OLD_FILES+=usr/share/mk/suite.test.mk +OLD_FILES+=usr/share/mk/sys.mk +OLD_FILES+=usr/share/mk/tap.test.mk +OLD_FILES+=usr/share/mk/version_gen.awk +OLD_FILES+=usr/tests/usr.bin/bmake/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/archives/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.status.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stderr.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/expected.stdout.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd/libtest.a +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.status.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stderr.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/expected.stdout.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_44bsd_mod/libtest.a +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.status.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stderr.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.3 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.4 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.5 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.6 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/expected.stdout.7 +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/archives/fmt_oldbsd/libtest.a +OLD_FILES+=usr/tests/usr.bin/bmake/basic/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t0/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t0/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t0/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t0/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t0/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t1/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t2/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t3/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t3/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t3/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t3/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/basic/t3/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/common.sh +OLD_FILES+=usr/tests/usr.bin/bmake/execution/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/ellipsis/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/empty/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/joberr/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/execution/plus/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/builtin/sh +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/meta/sh +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path/sh +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/path_select/shell +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/replace/shell +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/shell/select/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/TEST1.a +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/basic/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/TEST1.a +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/TEST2.a +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild1/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/TEST1.a +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/TEST2.a +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/suffixes/src_wild2/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/directive-t0/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.status.3 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.status.4 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.status.5 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stderr.3 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stderr.4 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stderr.5 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stdout.3 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stdout.4 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/expected.stdout.5 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/enl/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/funny-targets/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/syntax/semi/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/1/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/mk/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t0/mk/sys.mk +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/cleanup +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/1/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/mk/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t1/mk/sys.mk +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/cleanup +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/1/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/mk/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/sysmk/t2/mk/sys.mk +OLD_FILES+=usr/tests/usr.bin/bmake/test-new.mk +OLD_FILES+=usr/tests/usr.bin/bmake/variables/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_M/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.status.3 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stderr.3 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/expected.stdout.3 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/modifier_t/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.status.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.stderr.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/expected.stdout.2 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/opt_V/legacy_test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/Kyuafile +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/Makefile.test +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/expected.status.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/expected.stderr.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/expected.stdout.1 +OLD_FILES+=usr/tests/usr.bin/bmake/variables/t0/legacy_test +.endif +.if ${MK_MAN} == no +MAN_FILES!=find ${DESTDIR}/usr/share/man ${DESTDIR}/usr/share/openssl/man -type f | sed -e 's,^${DESTDIR}/,,'; echo +OLD_FILES+=${MAN_FILES} +.endif + +.if ${MK_MAN_UTILS} == no +OLD_FILES+=etc/periodic/weekly/320.whatis +OLD_FILES+=etc/periodic/weekly/330.catman +OLD_FILES+=usr/bin/apropos +OLD_FILES+=usr/bin/catman +OLD_FILES+=usr/bin/makewhatis +OLD_FILES+=usr/bin/man +OLD_FILES+=usr/bin/manpath +OLD_FILES+=usr/bin/whatis +OLD_FILES+=usr/libexec/catman.local +OLD_FILES+=usr/libexec/makewhatis.local +OLD_FILES+=usr/sbin/manctl +OLD_FILES+=usr/share/man/man1/apropos.1.gz +OLD_FILES+=usr/share/man/man1/catman.1.gz +OLD_FILES+=usr/share/man/man1/makewhatis.1.gz +OLD_FILES+=usr/share/man/man1/man.1.gz +OLD_FILES+=usr/share/man/man1/manpath.1.gz +OLD_FILES+=usr/share/man/man1/whatis.1.gz +OLD_FILES+=usr/share/man/man5/man.conf.5.gz +OLD_FILES+=usr/share/man/man8/catman.local.8.gz +OLD_FILES+=usr/share/man/man8/makewhatis.local.8.gz +OLD_FILES+=usr/share/man/man8/manctl.8.gz +OLD_FILES+=usr/share/man/whatis +OLD_FILES+=usr/share/openssl/man/whatis +.endif + .if ${MK_NDIS} == no OLD_FILES+=usr/sbin/ndiscvt OLD_FILES+=usr/sbin/ndisgen OLD_FILES+=usr/share/man/man8/ndiscvt.8.gz OLD_FILES+=usr/share/man/man8/ndisgen.8.gz OLD_FILES+=usr/share/misc/windrv_stub.c .endif .if ${MK_NFS_SERVER} == no OLD_FILES+=etc/rc.d/mountd OLD_FILES+=etc/rc.d/nfscbd OLD_FILES+=etc/rc.d/nfsd OLD_FILES+=etc/rc.d/nfsuserd OLD_FILES+=usr/sbin/nfscbd OLD_FILES+=usr/sbin/nfsd OLD_FILES+=usr/sbin/nfsdumpstate OLD_FILES+=usr/sbin/nfsrevoke OLD_FILES+=usr/sbin/nfsuserd OLD_FILES+=usr/share/man/man4/nfsv4.4.gz OLD_FILES+=usr/share/man/man5/exports.5.gz OLD_FILES+=usr/share/man/man5/netgroup.5.gz OLD_FILES+=usr/share/man/man5/stablerestart.5.gz OLD_FILES+=usr/share/man/man8/mountd.8.gz OLD_FILES+=usr/share/man/man8/nfscbd.8.gz OLD_FILES+=usr/share/man/man8/nfsd.8.gz OLD_FILES+=usr/share/man/man8/nfsdumpstate.8.gz OLD_FILES+=usr/share/man/man8/nfsrevoke.8.gz OLD_FILES+=usr/share/man/man8/nfsuserd.8.gz .endif .if ${MK_NETCAT} == no OLD_FILES+=usr/bin/nc OLD_FILES+=usr/share/man/man1/nc.1.gz .endif .if ${MK_NETGRAPH} == no -OLD_FILES+=usr/sbin/ngctl -OLD_FILES+=usr/sbin/nghook -OLD_FILES+=usr/share/examples/netgraph/ether.bridge -OLD_FILES+=usr/share/examples/netgraph/frame_relay -OLD_FILES+=usr/share/examples/netgraph/ngctl -OLD_FILES+=usr/share/examples/netgraph/raw -OLD_FILES+=usr/share/examples/netgraph/udp.tunnel -OLD_FILES+=usr/share/examples/netgraph/virtual.chain -OLD_FILES+=usr/share/examples/netgraph/virtual.lan -OLD_FILES+=usr/share/man/man4/ng_UI.4.gz -OLD_FILES+=usr/share/man/man4/ng_async.4.gz -OLD_FILES+=usr/share/man/man4/ng_atm.4.gz -OLD_FILES+=usr/share/man/man4/ng_atmllc.4.gz -OLD_FILES+=usr/share/man/man4/ng_bluetooth.4.gz -OLD_FILES+=usr/share/man/man4/ng_bpf.4.gz -OLD_FILES+=usr/share/man/man4/ng_bridge.4.gz -OLD_FILES+=usr/share/man/man4/ng_bt3c.4.gz -OLD_FILES+=usr/share/man/man4/ng_btsocket.4.gz -OLD_FILES+=usr/share/man/man4/ng_car.4.gz -OLD_FILES+=usr/share/man/man4/ng_ccatm.4.gz -OLD_FILES+=usr/share/man/man4/ng_cisco.4.gz -OLD_FILES+=usr/share/man/man4/ng_deflate.4.gz -OLD_FILES+=usr/share/man/man4/ng_device.4.gz -OLD_FILES+=usr/share/man/man4/ng_echo.4.gz -OLD_FILES+=usr/share/man/man4/ng_eiface.4.gz -OLD_FILES+=usr/share/man/man4/ng_etf.4.gz -OLD_FILES+=usr/share/man/man4/ng_ether.4.gz -OLD_FILES+=usr/share/man/man4/ng_ether_echo.4.gz -OLD_FILES+=usr/share/man/man4/ng_frame_relay.4.gz -OLD_FILES+=usr/share/man/man4/ng_gif.4.gz -OLD_FILES+=usr/share/man/man4/ng_gif_demux.4.gz -OLD_FILES+=usr/share/man/man4/ng_h4.4.gz -OLD_FILES+=usr/share/man/man4/ng_hci.4.gz -OLD_FILES+=usr/share/man/man4/ng_hole.4.gz -OLD_FILES+=usr/share/man/man4/ng_hub.4.gz -OLD_FILES+=usr/share/man/man4/ng_iface.4.gz -OLD_FILES+=usr/share/man/man4/ng_ip_input.4.gz -OLD_FILES+=usr/share/man/man4/ng_ipfw.4.gz -OLD_FILES+=usr/share/man/man4/ng_ksocket.4.gz -OLD_FILES+=usr/share/man/man4/ng_l2cap.4.gz -OLD_FILES+=usr/share/man/man4/ng_l2tp.4.gz -OLD_FILES+=usr/share/man/man4/ng_lmi.4.gz -OLD_FILES+=usr/share/man/man4/ng_mppc.4.gz -OLD_FILES+=usr/share/man/man4/ng_nat.4.gz -OLD_FILES+=usr/share/man/man4/ng_netflow.4.gz -OLD_FILES+=usr/share/man/man4/ng_one2many.4.gz -OLD_FILES+=usr/share/man/man4/ng_patch.4.gz -OLD_FILES+=usr/share/man/man4/ng_ppp.4.gz -OLD_FILES+=usr/share/man/man4/ng_pppoe.4.gz -OLD_FILES+=usr/share/man/man4/ng_pptpgre.4.gz -OLD_FILES+=usr/share/man/man4/ng_pred1.4.gz -OLD_FILES+=usr/share/man/man4/ng_rfc1490.4.gz -OLD_FILES+=usr/share/man/man4/ng_socket.4.gz -OLD_FILES+=usr/share/man/man4/ng_source.4.gz -OLD_FILES+=usr/share/man/man4/ng_split.4.gz -OLD_FILES+=usr/share/man/man4/ng_sppp.4.gz -OLD_FILES+=usr/share/man/man4/ng_sscfu.4.gz -OLD_FILES+=usr/share/man/man4/ng_sscop.4.gz -OLD_FILES+=usr/share/man/man4/ng_tag.4.gz -OLD_FILES+=usr/share/man/man4/ng_tcpmss.4.gz -OLD_FILES+=usr/share/man/man4/ng_tee.4.gz -OLD_FILES+=usr/share/man/man4/ng_tty.4.gz -OLD_FILES+=usr/share/man/man4/ng_ubt.4.gz -OLD_FILES+=usr/share/man/man4/ng_uni.4.gz -OLD_FILES+=usr/share/man/man4/ng_vjc.4.gz -OLD_FILES+=usr/share/man/man4/ng_vlan.4.gz -OLD_FILES+=usr/share/man/man8/ngctl.8.gz -OLD_FILES+=usr/share/man/man8/nghook.8.gz -OLD_DIRS+=usr/share/examples/netgraph -.endif - -.if ${MK_NETGRAPH_SUPPORT} == no -OLD_FILES+=usr/include/netgraph/atm/ng_atm.h -OLD_FILES+=usr/include/netgraph/atm/ng_ccatm.h -OLD_FILES+=usr/include/netgraph/atm/ng_sscfu.h -OLD_FILES+=usr/include/netgraph/atm/ng_sscop.h -OLD_FILES+=usr/include/netgraph/atm/ng_uni.h -OLD_FILES+=usr/include/netgraph/atm/ngatmbase.h -OLD_FILES+=usr/include/netgraph/netflow/netflow.h -OLD_FILES+=usr/include/netgraph/netflow/netflow_v9.h -OLD_FILES+=usr/include/netgraph/netflow/ng_netflow.h -OLD_FILES+=usr/include/netgraph/netgraph.h -OLD_FILES+=usr/include/netgraph/ng_UI.h -OLD_FILES+=usr/include/netgraph/ng_async.h -OLD_FILES+=usr/include/netgraph/ng_atmllc.h -OLD_FILES+=usr/include/netgraph/ng_bpf.h -OLD_FILES+=usr/include/netgraph/ng_bridge.h -OLD_FILES+=usr/include/netgraph/ng_car.h -OLD_FILES+=usr/include/netgraph/ng_cisco.h -OLD_FILES+=usr/include/netgraph/ng_deflate.h -OLD_FILES+=usr/include/netgraph/ng_device.h -OLD_FILES+=usr/include/netgraph/ng_echo.h -OLD_FILES+=usr/include/netgraph/ng_eiface.h -OLD_FILES+=usr/include/netgraph/ng_etf.h -OLD_FILES+=usr/include/netgraph/ng_ether.h -OLD_FILES+=usr/include/netgraph/ng_ether_echo.h -OLD_FILES+=usr/include/netgraph/ng_frame_relay.h -OLD_FILES+=usr/include/netgraph/ng_gif.h -OLD_FILES+=usr/include/netgraph/ng_gif_demux.h -OLD_FILES+=usr/include/netgraph/ng_hole.h -OLD_FILES+=usr/include/netgraph/ng_hub.h -OLD_FILES+=usr/include/netgraph/ng_iface.h -OLD_FILES+=usr/include/netgraph/ng_ip_input.h -OLD_FILES+=usr/include/netgraph/ng_ipfw.h -OLD_FILES+=usr/include/netgraph/ng_ksocket.h -OLD_FILES+=usr/include/netgraph/ng_l2tp.h -OLD_FILES+=usr/include/netgraph/ng_lmi.h -OLD_FILES+=usr/include/netgraph/ng_message.h -OLD_FILES+=usr/include/netgraph/ng_mppc.h -OLD_FILES+=usr/include/netgraph/ng_nat.h -OLD_FILES+=usr/include/netgraph/ng_one2many.h -OLD_FILES+=usr/include/netgraph/ng_parse.h -OLD_FILES+=usr/include/netgraph/ng_patch.h -OLD_FILES+=usr/include/netgraph/ng_pipe.h -OLD_FILES+=usr/include/netgraph/ng_ppp.h -OLD_FILES+=usr/include/netgraph/ng_pppoe.h -OLD_FILES+=usr/include/netgraph/ng_pptpgre.h -OLD_FILES+=usr/include/netgraph/ng_pred1.h -OLD_FILES+=usr/include/netgraph/ng_rfc1490.h -OLD_FILES+=usr/include/netgraph/ng_sample.h -OLD_FILES+=usr/include/netgraph/ng_socket.h -OLD_FILES+=usr/include/netgraph/ng_socketvar.h -OLD_FILES+=usr/include/netgraph/ng_source.h -OLD_FILES+=usr/include/netgraph/ng_split.h -OLD_FILES+=usr/include/netgraph/ng_sppp.h -OLD_FILES+=usr/include/netgraph/ng_tag.h -OLD_FILES+=usr/include/netgraph/ng_tcpmss.h -OLD_FILES+=usr/include/netgraph/ng_tee.h -OLD_FILES+=usr/include/netgraph/ng_tty.h -OLD_FILES+=usr/include/netgraph/ng_vjc.h -OLD_FILES+=usr/include/netgraph/ng_vlan.h OLD_FILES+=usr/include/netgraph.h OLD_FILES+=usr/lib/libnetgraph.a +OLD_FILES+=usr/lib/libnetgraph.so OLD_LIBS+=usr/lib/libnetgraph.so.4 OLD_FILES+=usr/lib/libnetgraph_p.a +OLD_FILES+=usr/lib32/libnetgraph.a +OLD_FILES+=usr/lib32/libnetgraph.so +OLD_LIBS+=usr/lib32/libnetgraph.so.4 +OLD_FILES+=usr/lib32/libnetgraph_p.a +OLD_FILES+=usr/libexec/pppoed +OLD_FILES+=usr/sbin/flowctl +OLD_FILES+=usr/sbin/lmcconfig +OLD_FILES+=usr/sbin/ngctl +OLD_FILES+=usr/sbin/nghook OLD_FILES+=usr/share/man/man3/NgAllocRecvAsciiMsg.3.gz OLD_FILES+=usr/share/man/man3/NgAllocRecvData.3.gz OLD_FILES+=usr/share/man/man3/NgAllocRecvMsg.3.gz OLD_FILES+=usr/share/man/man3/NgMkSockNode.3.gz OLD_FILES+=usr/share/man/man3/NgNameNode.3.gz OLD_FILES+=usr/share/man/man3/NgRecvAsciiMsg.3.gz OLD_FILES+=usr/share/man/man3/NgRecvData.3.gz OLD_FILES+=usr/share/man/man3/NgRecvMsg.3.gz OLD_FILES+=usr/share/man/man3/NgSendAsciiMsg.3.gz OLD_FILES+=usr/share/man/man3/NgSendData.3.gz OLD_FILES+=usr/share/man/man3/NgSendMsg.3.gz OLD_FILES+=usr/share/man/man3/NgSendMsgReply.3.gz OLD_FILES+=usr/share/man/man3/NgSetDebug.3.gz OLD_FILES+=usr/share/man/man3/NgSetErrLog.3.gz OLD_FILES+=usr/share/man/man3/netgraph.3.gz -OLD_DIRS+=usr/include/netgraph/atm -OLD_DIRS+=usr/include/netgraph/netflow -OLD_DIRS+=usr/include/netgraph +OLD_FILES+=usr/share/man/man8/flowctl.8.gz +OLD_FILES+=usr/share/man/man8/lmcconfig.8.gz +OLD_FILES+=usr/share/man/man8/ngctl.8.gz +OLD_FILES+=usr/share/man/man8/nghook.8.gz +OLD_FILES+=usr/share/man/man8/pppoed.8.gz .endif +.if ${MK_NETGRAPH_SUPPORT} == no +OLD_FILES+=usr/include/bsnmp/snmp_netgraph.h +OLD_FILES+=usr/lib/snmp_netgraph.so +OLD_LIBS+=usr/lib/snmp_netgraph.so.6 +OLD_FILES+=usr/share/man/man3/snmp_netgraph.3.gz +OLD_FILES+=usr/share/snmp/defs/netgraph_tree.def +OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-NETGRAPH.txt +.endif + .if ${MK_NIS} == no OLD_FILES+=usr/bin/ypcat OLD_FILES+=usr/bin/ypchfn OLD_FILES+=usr/bin/ypchpass OLD_FILES+=usr/bin/ypchsh OLD_FILES+=usr/bin/ypmatch OLD_FILES+=usr/bin/yppasswd OLD_FILES+=usr/bin/ypwhich OLD_FILES+=usr/include/ypclnt.h OLD_FILES+=usr/lib/libypclnt.a OLD_FILES+=usr/lib/libypclnt.so OLD_LIBS+=usr/lib/libypclnt.so.4 OLD_FILES+=usr/lib/libypclnt_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libypclnt.a OLD_FILES+=usr/lib32/libypclnt.so OLD_LIBS+=usr/lib32/libypclnt.so.4 OLD_FILES+=usr/lib32/libypclnt_p.a .endif OLD_FILES+=usr/libexec/mknetid OLD_FILES+=usr/libexec/yppwupdate OLD_FILES+=usr/libexec/ypxfr OLD_FILES+=usr/sbin/rpc.yppasswdd OLD_FILES+=usr/sbin/rpc.ypupdated OLD_FILES+=usr/sbin/rpc.ypxfrd OLD_FILES+=usr/sbin/yp_mkdb OLD_FILES+=usr/sbin/ypbind OLD_FILES+=usr/sbin/ypinit OLD_FILES+=usr/sbin/yppoll OLD_FILES+=usr/sbin/yppush OLD_FILES+=usr/sbin/ypserv OLD_FILES+=usr/sbin/ypset OLD_FILES+=usr/share/man/man1/ypcat.1.gz OLD_FILES+=usr/share/man/man1/ypchfn.1.gz OLD_FILES+=usr/share/man/man1/ypchpass.1.gz OLD_FILES+=usr/share/man/man1/ypchsh.1.gz OLD_FILES+=usr/share/man/man1/ypmatch.1.gz OLD_FILES+=usr/share/man/man1/yppasswd.1.gz OLD_FILES+=usr/share/man/man1/ypwhich.1.gz OLD_FILES+=usr/share/man/man5/netid.5.gz OLD_FILES+=usr/share/man/man8/mknetid.8.gz OLD_FILES+=usr/share/man/man8/rpc.yppasswdd.8.gz OLD_FILES+=usr/share/man/man8/rpc.ypxfrd.8.gz OLD_FILES+=usr/share/man/man8/yp_mkdb.8.gz OLD_FILES+=usr/share/man/man8/ypbind.8.gz OLD_FILES+=usr/share/man/man8/ypinit.8.gz OLD_FILES+=usr/share/man/man8/yppoll.8.gz OLD_FILES+=usr/share/man/man8/yppush.8.gz OLD_FILES+=usr/share/man/man8/ypserv.8.gz OLD_FILES+=usr/share/man/man8/ypset.8.gz OLD_FILES+=usr/share/man/man8/ypxfr.8.gz OLD_FILES+=var/yp/Makefile OLD_FILES+=var/yp/Makefile.dist .endif .if ${MK_NLS} == no +OLD_FILES+=usr/share/nls/C/ee.cat +OLD_FILES+=usr/share/nls/be_BY.UTF-8/libc.cat +OLD_FILES+=usr/share/nls/ca_ES.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/de_AT.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/de_AT.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/de_AT.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/de_AT.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/de_AT.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/de_CH.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/de_CH.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/de_CH.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/de_CH.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/de_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/de_DE.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/de_DE.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/de_DE.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/de_DE.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/de_DE.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/de_DE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/el_GR.ISO8859-7/libc.cat +OLD_FILES+=usr/share/nls/el_GR.ISO8859-7/tcsh.cat +OLD_FILES+=usr/share/nls/el_GR.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/en_US.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/en_US.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/es_ES.ISO8859-1/grep.cat +OLD_FILES+=usr/share/nls/es_ES.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/es_ES.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/es_ES.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/es_ES.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/et_EE.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/et_EE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fi_FI.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/fi_FI.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/fi_FI.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/fi_FI.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_BE.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/fr_BE.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/fr_BE.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/fr_BE.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/fr_BE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CA.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/fr_CA.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CA.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/fr_CA.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CA.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CH.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/fr_CH.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CH.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/fr_CH.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_FR.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/fr_FR.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/fr_FR.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/fr_FR.ISO8859-15/ee.cat +OLD_FILES+=usr/share/nls/fr_FR.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/fr_FR.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/gl_ES.ISO8859-1/grep.cat +OLD_FILES+=usr/share/nls/gl_ES.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/hu_HU.ISO8859-2/ee.cat +OLD_FILES+=usr/share/nls/hu_HU.ISO8859-2/grep.cat +OLD_FILES+=usr/share/nls/hu_HU.ISO8859-2/libc.cat +OLD_FILES+=usr/share/nls/hu_HU.ISO8859-2/sort.cat +OLD_FILES+=usr/share/nls/it_CH.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/it_CH.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/it_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/it_IT.ISO8859-1/tcsh.cat +OLD_FILES+=usr/share/nls/it_IT.ISO8859-15/libc.cat +OLD_FILES+=usr/share/nls/it_IT.ISO8859-15/tcsh.cat +OLD_FILES+=usr/share/nls/it_IT.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/ja_JP.SJIS/grep.cat +OLD_FILES+=usr/share/nls/ja_JP.SJIS/tcsh.cat +OLD_FILES+=usr/share/nls/ja_JP.UTF-8/grep.cat +OLD_FILES+=usr/share/nls/ja_JP.UTF-8/libc.cat +OLD_FILES+=usr/share/nls/ja_JP.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/ja_JP.eucJP/grep.cat +OLD_FILES+=usr/share/nls/ja_JP.eucJP/libc.cat +OLD_FILES+=usr/share/nls/ja_JP.eucJP/tcsh.cat +OLD_FILES+=usr/share/nls/ko_KR.UTF-8/libc.cat +OLD_FILES+=usr/share/nls/ko_KR.eucKR/libc.cat +OLD_FILES+=usr/share/nls/mn_MN.UTF-8/libc.cat +OLD_FILES+=usr/share/nls/nl_NL.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/no_NO.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/pl_PL.ISO8859-2/ee.cat +OLD_FILES+=usr/share/nls/pl_PL.ISO8859-2/libc.cat +OLD_FILES+=usr/share/nls/pt_BR.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/pt_BR.ISO8859-1/grep.cat +OLD_FILES+=usr/share/nls/pt_BR.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/pt_PT.ISO8859-1/ee.cat +OLD_FILES+=usr/share/nls/ru_RU.CP1251/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.CP866/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.ISO8859-5/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/ee.cat +OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/grep.cat +OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/libc.cat +OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/sk_SK.ISO8859-2/libc.cat +OLD_FILES+=usr/share/nls/sv_SE.ISO8859-1/libc.cat +OLD_FILES+=usr/share/nls/uk_UA.ISO8859-5/tcsh.cat +OLD_FILES+=usr/share/nls/uk_UA.KOI8-U/ee.cat +OLD_FILES+=usr/share/nls/uk_UA.KOI8-U/tcsh.cat +OLD_FILES+=usr/share/nls/uk_UA.UTF-8/grep.cat +OLD_FILES+=usr/share/nls/uk_UA.UTF-8/libc.cat +OLD_FILES+=usr/share/nls/uk_UA.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/zh_CN.GB18030/libc.cat +OLD_FILES+=usr/share/nls/zh_CN.GB2312/libc.cat +OLD_FILES+=usr/share/nls/zh_CN.UTF-8/grep.cat +OLD_FILES+=usr/share/nls/zh_CN.UTF-8/libc.cat OLD_FILES+=usr/tests/bin/sh/builtins/locale1.0 -# to be filled in .endif +.if ${MK_NLS_CATALOGS} == no +OLD_FILES+=usr/share/nls/de_AT.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/de_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/de_DE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/el_GR.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/es_ES.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/et_EE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fi_FI.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_BE.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CA.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/fr_FR.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/it_CH.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/it_IT.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/ja_JP.SJIS/tcsh.cat +OLD_FILES+=usr/share/nls/ja_JP.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.CP1251/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.CP866/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.ISO8859-5/tcsh.cat +OLD_FILES+=usr/share/nls/ru_RU.UTF-8/tcsh.cat +OLD_FILES+=usr/share/nls/uk_UA.ISO8859-5/tcsh.cat +OLD_FILES+=usr/share/nls/uk_UA.UTF-8/tcsh.cat +.endif + +.if ${MK_NS_CACHING} == no +OLD_FILES+=etc/nscd.conf +OLD_FILES+=etc/rc.d/nscd +OLD_FILES+=usr/sbin/nscd +OLD_FILES+=usr/share/examples/etc/nscd.conf +OLD_FILES+=usr/share/man/man5/nscd.conf.5.gz +OLD_FILES+=usr/share/man/man8/nscd.8.gz +.endif + .if ${MK_NTP} == no OLD_FILES+=etc/ntp.conf OLD_DIRS+=etc/ntp OLD_FILES+=etc/rc.d/ntpd OLD_FILES+=etc/rc.d/ntpdate OLD_FILES+=etc/periodic/daily/480.status-ntpd OLD_FILES+=usr/bin/ntpq OLD_FILES+=usr/sbin/ntp-keygen OLD_FILES+=usr/sbin/ntpd OLD_FILES+=usr/sbin/ntpdate OLD_FILES+=usr/sbin/ntpdc OLD_FILES+=usr/sbin/ntptime OLD_FILES+=usr/sbin/sntp OLD_FILES+=usr/share/doc/ntp/accopt.html OLD_FILES+=usr/share/doc/ntp/assoc.html OLD_FILES+=usr/share/doc/ntp/audio.html OLD_FILES+=usr/share/doc/ntp/authopt.html OLD_FILES+=usr/share/doc/ntp/build.html OLD_FILES+=usr/share/doc/ntp/clockopt.html OLD_FILES+=usr/share/doc/ntp/config.html OLD_FILES+=usr/share/doc/ntp/confopt.html OLD_FILES+=usr/share/doc/ntp/copyright.html OLD_FILES+=usr/share/doc/ntp/debug.html OLD_FILES+=usr/share/doc/ntp/driver1.html OLD_FILES+=usr/share/doc/ntp/driver10.html OLD_FILES+=usr/share/doc/ntp/driver11.html OLD_FILES+=usr/share/doc/ntp/driver12.html OLD_FILES+=usr/share/doc/ntp/driver16.html OLD_FILES+=usr/share/doc/ntp/driver18.html OLD_FILES+=usr/share/doc/ntp/driver19.html OLD_FILES+=usr/share/doc/ntp/driver2.html OLD_FILES+=usr/share/doc/ntp/driver20.html OLD_FILES+=usr/share/doc/ntp/driver22.html OLD_FILES+=usr/share/doc/ntp/driver26.html OLD_FILES+=usr/share/doc/ntp/driver27.html OLD_FILES+=usr/share/doc/ntp/driver28.html OLD_FILES+=usr/share/doc/ntp/driver29.html OLD_FILES+=usr/share/doc/ntp/driver3.html OLD_FILES+=usr/share/doc/ntp/driver30.html OLD_FILES+=usr/share/doc/ntp/driver32.html OLD_FILES+=usr/share/doc/ntp/driver33.html OLD_FILES+=usr/share/doc/ntp/driver34.html OLD_FILES+=usr/share/doc/ntp/driver35.html OLD_FILES+=usr/share/doc/ntp/driver36.html OLD_FILES+=usr/share/doc/ntp/driver37.html OLD_FILES+=usr/share/doc/ntp/driver4.html OLD_FILES+=usr/share/doc/ntp/driver5.html OLD_FILES+=usr/share/doc/ntp/driver6.html OLD_FILES+=usr/share/doc/ntp/driver7.html OLD_FILES+=usr/share/doc/ntp/driver8.html OLD_FILES+=usr/share/doc/ntp/driver9.html OLD_FILES+=usr/share/doc/ntp/extern.html OLD_FILES+=usr/share/doc/ntp/hints.html OLD_FILES+=usr/share/doc/ntp/howto.html OLD_FILES+=usr/share/doc/ntp/index.html OLD_FILES+=usr/share/doc/ntp/kern.html OLD_FILES+=usr/share/doc/ntp/ldisc.html OLD_FILES+=usr/share/doc/ntp/measure.html OLD_FILES+=usr/share/doc/ntp/miscopt.html OLD_FILES+=usr/share/doc/ntp/monopt.html OLD_FILES+=usr/share/doc/ntp/mx4200data.html OLD_FILES+=usr/share/doc/ntp/notes.html OLD_FILES+=usr/share/doc/ntp/ntpd.html OLD_FILES+=usr/share/doc/ntp/ntpdate.html OLD_FILES+=usr/share/doc/ntp/ntpdc.html OLD_FILES+=usr/share/doc/ntp/ntpq.html OLD_FILES+=usr/share/doc/ntp/ntptime.html OLD_FILES+=usr/share/doc/ntp/ntptrace.html OLD_FILES+=usr/share/doc/ntp/parsedata.html OLD_FILES+=usr/share/doc/ntp/parsenew.html OLD_FILES+=usr/share/doc/ntp/patches.html OLD_FILES+=usr/share/doc/ntp/porting.html OLD_FILES+=usr/share/doc/ntp/pps.html OLD_FILES+=usr/share/doc/ntp/prefer.html OLD_FILES+=usr/share/doc/ntp/quick.html OLD_FILES+=usr/share/doc/ntp/rdebug.html OLD_FILES+=usr/share/doc/ntp/refclock.html OLD_FILES+=usr/share/doc/ntp/release.html OLD_FILES+=usr/share/doc/ntp/tickadj.html OLD_DIRS+=usr/share/doc/ntp OLD_FILES+=usr/share/examples/etc/ntp.conf +OLD_FILES+=usr/share/man/man1/sntp.1.gz OLD_FILES+=usr/share/man/man5/ntp.conf.5.gz OLD_FILES+=usr/share/man/man5/ntp.keys.5.gz OLD_FILES+=usr/share/man/man8/ntp-keygen.8.gz OLD_FILES+=usr/share/man/man8/ntpd.8.gz OLD_FILES+=usr/share/man/man8/ntpdate.8.gz OLD_FILES+=usr/share/man/man8/ntpdc.8.gz OLD_FILES+=usr/share/man/man8/ntpq.8.gz OLD_FILES+=usr/share/man/man8/ntptime.8.gz .endif #.if ${MK_OBJC} == no # to be filled in #.endif .if ${MK_OPENSSH} == no -OLD_FILES+=etc/pam.d/sshd +OLD_FILES+=etc/rc.d/sshd +OLD_FILES+=etc/ssh/moduli +OLD_FILES+=etc/ssh/ssh_config +OLD_FILES+=etc/ssh/sshd_config +OLD_FILES+=usr/bin/scp OLD_FILES+=usr/bin/sftp +OLD_FILES+=usr/bin/slogin OLD_FILES+=usr/bin/ssh OLD_FILES+=usr/bin/ssh-add OLD_FILES+=usr/bin/ssh-agent OLD_FILES+=usr/bin/ssh-copy-id OLD_FILES+=usr/bin/ssh-keygen OLD_FILES+=usr/bin/ssh-keyscan +OLD_FILES+=usr/lib/pam_ssh.so +OLD_LIBS+=usr/lib/pam_ssh.so.5 OLD_FILES+=usr/lib/private/libssh.a OLD_FILES+=usr/lib/private/libssh.so OLD_LIBS+=usr/lib/private/libssh.so.5 OLD_FILES+=usr/lib/private/libssh_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" +OLD_FILES+=usr/lib32/pam_ssh.so +OLD_LIBS+=usr/lib32/pam_ssh.so.5 OLD_FILES+=usr/lib32/private/libssh.a OLD_FILES+=usr/lib32/private/libssh.so OLD_LIBS+=usr/lib32/private/libssh.so.5 OLD_FILES+=usr/lib32/private/libssh_p.a .endif OLD_FILES+=usr/libexec/sftp-server OLD_FILES+=usr/libexec/ssh-keysign OLD_FILES+=usr/libexec/ssh-pkcs11-helper OLD_FILES+=usr/sbin/sshd +OLD_FILES+=usr/share/man/man1/scp.1.gz +OLD_FILES+=usr/share/man/man1/sftp.1.gz +OLD_FILES+=usr/share/man/man1/slogin.1.gz +OLD_FILES+=usr/share/man/man1/ssh-add.1.gz +OLD_FILES+=usr/share/man/man1/ssh-agent.1.gz +OLD_FILES+=usr/share/man/man1/ssh-copy-id.1.gz +OLD_FILES+=usr/share/man/man1/ssh-keygen.1.gz +OLD_FILES+=usr/share/man/man1/ssh-keyscan.1.gz +OLD_FILES+=usr/share/man/man1/ssh.1.gz +OLD_FILES+=usr/share/man/man5/ssh_config.5.gz +OLD_FILES+=usr/share/man/man5/sshd_config.5.gz +OLD_FILES+=usr/share/man/man8/pam_ssh.8.gz +OLD_FILES+=usr/share/man/man8/sftp-server.8.gz +OLD_FILES+=usr/share/man/man8/ssh-keysign.8.gz +OLD_FILES+=usr/share/man/man8/ssh-pkcs11-helper.8.gz +OLD_FILES+=usr/share/man/man8/sshd.8.gz .endif .if ${MK_OPENSSL} == no OLD_FILES+=etc/rc.d/keyserv .endif .if ${MK_PC_SYSINSTALL} == no # backend-partmanager OLD_FILES+=usr/share/pc-sysinstall/backend-partmanager/create-part.sh OLD_FILES+=usr/share/pc-sysinstall/backend-partmanager/delete-part.sh # backend-query OLD_FILES+=usr/share/pc-sysinstall/backend-query/detect-emulation.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/detect-laptop.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/detect-nics.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/disk-info.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/disk-list.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/disk-part.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/enable-net.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/get-packages.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-components.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-config.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-mirrors.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-packages.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-rsync-backups.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/list-tzones.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/query-langs.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/send-logs.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/setup-ssh-keys.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/set-mirror.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/sys-mem.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/test-live.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/test-netup.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/update-part-list.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/xkeyboard-layouts.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/xkeyboard-models.sh OLD_FILES+=usr/share/pc-sysinstall/backend-query/xkeyboard-variants.sh # backend OLD_FILES+=usr/share/pc-sysinstall/backend/functions-bsdlabel.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-cleanup.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-disk.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-extractimage.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-ftp.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-installcomponents.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-installpackages.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-localize.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-mountdisk.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-mountoptical.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-networking.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-newfs.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-parse.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-packages.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-runcommands.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-unmount.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-upgrade.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions-users.sh OLD_FILES+=usr/share/pc-sysinstall/backend/functions.sh OLD_FILES+=usr/share/pc-sysinstall/backend/installimage.sh OLD_FILES+=usr/share/pc-sysinstall/backend/parseconfig.sh OLD_FILES+=usr/share/pc-sysinstall/backend/startautoinstall.sh # conf OLD_FILES+=usr/share/pc-sysinstall/conf/avail-langs OLD_FILES+=usr/share/pc-sysinstall/conf/exclude-from-upgrade OLD_FILES+=usr/share/pc-sysinstall/conf/license/bsd-en.txt OLD_FILES+=usr/share/pc-sysinstall/conf/license/intel-en.txt OLD_FILES+=usr/share/pc-sysinstall/conf/license/nvidia-en.txt OLD_FILES+=usr/share/pc-sysinstall/conf/pc-sysinstall.conf # doc OLD_FILES+=usr/share/pc-sysinstall/doc/help-disk-list OLD_FILES+=usr/share/pc-sysinstall/doc/help-disk-size OLD_FILES+=usr/share/pc-sysinstall/doc/help-index OLD_FILES+=usr/share/pc-sysinstall/doc/help-start-autoinstall # examples OLD_FILES+=usr/share/examples/pc-sysinstall/README OLD_FILES+=usr/share/examples/pc-sysinstall/pc-autoinstall.conf OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.fbsd-netinstall OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.geli OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.gmirror OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.netinstall OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.restore OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.rsync OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.upgrade OLD_FILES+=usr/share/examples/pc-sysinstall/pcinstall.cfg.zfs # pc-sysinstall OLD_FILES+=usr/sbin/pc-sysinstall OLD_FILES+=usr/share/man/man8/pc-sysinstall.8.gz OLD_DIRS+=usr/share/pc-sysinstall/backend OLD_DIRS+=usr/share/pc-sysinstall/backend-partmanager OLD_DIRS+=usr/share/pc-sysinstall/backend-query OLD_DIRS+=usr/share/pc-sysinstall/conf/license OLD_DIRS+=usr/share/pc-sysinstall/conf OLD_DIRS+=usr/share/pc-sysinstall/doc OLD_DIRS+=usr/share/pc-sysinstall OLD_DIRS+=usr/share/examples/pc-sysinstall .endif .if ${MK_PMC} == no OLD_FILES+=usr/include/pmc.h OLD_FILES+=usr/include/pmclog.h OLD_FILES+=usr/lib/libpmc.a OLD_LIBS+=usr/lib/libpmc.so.5 OLD_FILES+=usr/lib/libpmc_p.a OLD_FILES+=usr/sbin/pmcannotate OLD_FILES+=usr/sbin/pmccontrol OLD_FILES+=usr/sbin/pmcstat OLD_FILES+=usr/share/man/man3/pmc.3.gz OLD_FILES+=usr/share/man/man3/pmc.atom.3.gz OLD_FILES+=usr/share/man/man3/pmc.atomsilvermont.3.gz OLD_FILES+=usr/share/man/man3/pmc.core.3.gz OLD_FILES+=usr/share/man/man3/pmc.core2.3.gz OLD_FILES+=usr/share/man/man3/pmc.corei7.3.gz OLD_FILES+=usr/share/man/man3/pmc.corei7uc.3.gz OLD_FILES+=usr/share/man/man3/pmc.haswell.3.gz OLD_FILES+=usr/share/man/man3/pmc.haswelluc.3.gz OLD_FILES+=usr/share/man/man3/pmc.iaf.3.gz OLD_FILES+=usr/share/man/man3/pmc.ivybridge.3.gz OLD_FILES+=usr/share/man/man3/pmc.ivybridgexeon.3.gz OLD_FILES+=usr/share/man/man3/pmc.k7.3.gz OLD_FILES+=usr/share/man/man3/pmc.k8.3.gz OLD_FILES+=usr/share/man/man3/pmc.mips24k.3.gz OLD_FILES+=usr/share/man/man3/pmc.octeon.3.gz OLD_FILES+=usr/share/man/man3/pmc.p4.3.gz OLD_FILES+=usr/share/man/man3/pmc.p5.3.gz OLD_FILES+=usr/share/man/man3/pmc.p6.3.gz OLD_FILES+=usr/share/man/man3/pmc.sandybridge.3.gz OLD_FILES+=usr/share/man/man3/pmc.sandybridgeuc.3.gz OLD_FILES+=usr/share/man/man3/pmc.sandybridgexeon.3.gz OLD_FILES+=usr/share/man/man3/pmc.soft.3.gz OLD_FILES+=usr/share/man/man3/pmc.tsc.3.gz OLD_FILES+=usr/share/man/man3/pmc.ucf.3.gz OLD_FILES+=usr/share/man/man3/pmc.westmere.3.gz OLD_FILES+=usr/share/man/man3/pmc.westmereuc.3.gz OLD_FILES+=usr/share/man/man3/pmc.xscale.3.gz OLD_FILES+=usr/share/man/man3/pmc_allocate.3.gz OLD_FILES+=usr/share/man/man3/pmc_attach.3.gz OLD_FILES+=usr/share/man/man3/pmc_capabilities.3.gz OLD_FILES+=usr/share/man/man3/pmc_configure_logfile.3.gz OLD_FILES+=usr/share/man/man3/pmc_cpuinfo.3.gz OLD_FILES+=usr/share/man/man3/pmc_detach.3.gz OLD_FILES+=usr/share/man/man3/pmc_disable.3.gz OLD_FILES+=usr/share/man/man3/pmc_enable.3.gz OLD_FILES+=usr/share/man/man3/pmc_event_names_of_class.3.gz OLD_FILES+=usr/share/man/man3/pmc_flush_logfile.3.gz OLD_FILES+=usr/share/man/man3/pmc_get_driver_stats.3.gz OLD_FILES+=usr/share/man/man3/pmc_get_msr.3.gz OLD_FILES+=usr/share/man/man3/pmc_init.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_capability.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_class.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_cputype.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_disposition.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_event.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_mode.3.gz OLD_FILES+=usr/share/man/man3/pmc_name_of_state.3.gz OLD_FILES+=usr/share/man/man3/pmc_ncpu.3.gz OLD_FILES+=usr/share/man/man3/pmc_npmc.3.gz OLD_FILES+=usr/share/man/man3/pmc_pmcinfo.3.gz OLD_FILES+=usr/share/man/man3/pmc_read.3.gz OLD_FILES+=usr/share/man/man3/pmc_release.3.gz OLD_FILES+=usr/share/man/man3/pmc_rw.3.gz OLD_FILES+=usr/share/man/man3/pmc_set.3.gz OLD_FILES+=usr/share/man/man3/pmc_start.3.gz OLD_FILES+=usr/share/man/man3/pmc_stop.3.gz OLD_FILES+=usr/share/man/man3/pmc_width.3.gz OLD_FILES+=usr/share/man/man3/pmc_write.3.gz OLD_FILES+=usr/share/man/man3/pmc_writelog.3.gz OLD_FILES+=usr/share/man/man3/pmclog.3.gz OLD_FILES+=usr/share/man/man3/pmclog_close.3.gz OLD_FILES+=usr/share/man/man3/pmclog_feed.3.gz OLD_FILES+=usr/share/man/man3/pmclog_open.3.gz OLD_FILES+=usr/share/man/man3/pmclog_read.3.gz OLD_FILES+=usr/share/man/man8/pmcannotate.8.gz OLD_FILES+=usr/share/man/man8/pmccontrol.8.gz OLD_FILES+=usr/share/man/man8/pmcstat.8.gz .endif .if ${MK_PF} == no OLD_FILES+=etc/periodic/security/520.pfdenied OLD_FILES+=etc/pf.os OLD_FILES+=etc/rc.d/ftp-proxy OLD_FILES+=sbin/pfctl OLD_FILES+=sbin/pflogd +OLD_FILES+=usr/include/netpfil/pf/pf.h +OLD_FILES+=usr/include/netpfil/pf/pf_altq.h +OLD_FILES+=usr/include/netpfil/pf/pf_mtag.h +OLD_FILES+=usr/lib/snmp_pf.so +OLD_LIBS+=usr/lib/snmp_pf.so.6 OLD_FILES+=usr/libexec/tftp-proxy OLD_FILES+=usr/sbin/ftp-proxy OLD_FILES+=usr/share/examples/etc/pf.os OLD_FILES+=usr/share/examples/pf/ackpri OLD_FILES+=usr/share/examples/pf/faq-example1 OLD_FILES+=usr/share/examples/pf/faq-example2 OLD_FILES+=usr/share/examples/pf/faq-example3 OLD_FILES+=usr/share/examples/pf/pf.conf OLD_FILES+=usr/share/examples/pf/queue1 OLD_FILES+=usr/share/examples/pf/queue2 OLD_FILES+=usr/share/examples/pf/queue3 OLD_FILES+=usr/share/examples/pf/queue4 OLD_FILES+=usr/share/examples/pf/spamd OLD_DIRS+=usr/share/examples/pf OLD_FILES+=usr/share/man/man4/pf.4.gz OLD_FILES+=usr/share/man/man4/pflog.4.gz OLD_FILES+=usr/share/man/man4/pfsync.4.gz OLD_FILES+=usr/share/man/man5/pf.conf.5.gz OLD_FILES+=usr/share/man/man5/pf.os.5.gz OLD_FILES+=usr/share/man/man8/ftp-proxy.8.gz OLD_FILES+=usr/share/man/man8/pfctl.8.gz OLD_FILES+=usr/share/man/man8/pflogd.8.gz OLD_FILES+=usr/share/man/man8/tftp-proxy.8.gz +OLD_FILES+=usr/share/snmp/defs/pf_tree.def +OLD_FILES+=usr/share/snmp/mibs/BEGEMOT-PF-MIB.txt .endif .if ${MK_PKGBOOTSTRAP} == no OLD_FILES+=etc/pkg/FreeBSD.conf OLD_FILES+=usr/sbin/pkg OLD_FILES+=usr/share/man/man7/pkg.7.gz .endif +.if ${MK_PMC} == no +OLD_FILES+=usr/bin/pmcstudy +OLD_FILES+=usr/include/pmc.h +OLD_FILES+=usr/include/pmclog.h +OLD_FILES+=usr/lib/libpmc.a +OLD_FILES+=usr/lib/libpmc.so +OLD_LIBS+=usr/lib/libpmc.so.5 +OLD_FILES+=usr/lib/libpmc_p.a +OLD_FILES+=usr/lib32/libpmc.a +OLD_FILES+=usr/lib32/libpmc.so +OLD_LIBS+=usr/lib32/libpmc.so.5 +OLD_FILES+=usr/lib32/libpmc_p.a +OLD_FILES+=usr/sbin/pmcannotate +OLD_FILES+=usr/sbin/pmccontrol +OLD_FILES+=usr/sbin/pmcstat +OLD_FILES+=usr/share/man/man1/pmcstudy.1.gz +OLD_FILES+=usr/share/man/man3/pmc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.atom.3.gz +OLD_FILES+=usr/share/man/man3/pmc.atomsilvermont.3.gz +OLD_FILES+=usr/share/man/man3/pmc.core.3.gz +OLD_FILES+=usr/share/man/man3/pmc.core2.3.gz +OLD_FILES+=usr/share/man/man3/pmc.corei7.3.gz +OLD_FILES+=usr/share/man/man3/pmc.corei7uc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.haswell.3.gz +OLD_FILES+=usr/share/man/man3/pmc.haswelluc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.iaf.3.gz +OLD_FILES+=usr/share/man/man3/pmc.ivybridge.3.gz +OLD_FILES+=usr/share/man/man3/pmc.ivybridgexeon.3.gz +OLD_FILES+=usr/share/man/man3/pmc.k7.3.gz +OLD_FILES+=usr/share/man/man3/pmc.k8.3.gz +OLD_FILES+=usr/share/man/man3/pmc.mips24k.3.gz +OLD_FILES+=usr/share/man/man3/pmc.octeon.3.gz +OLD_FILES+=usr/share/man/man3/pmc.p4.3.gz +OLD_FILES+=usr/share/man/man3/pmc.p5.3.gz +OLD_FILES+=usr/share/man/man3/pmc.p6.3.gz +OLD_FILES+=usr/share/man/man3/pmc.sandybridge.3.gz +OLD_FILES+=usr/share/man/man3/pmc.sandybridgeuc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.sandybridgexeon.3.gz +OLD_FILES+=usr/share/man/man3/pmc.soft.3.gz +OLD_FILES+=usr/share/man/man3/pmc.tsc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.ucf.3.gz +OLD_FILES+=usr/share/man/man3/pmc.westmere.3.gz +OLD_FILES+=usr/share/man/man3/pmc.westmereuc.3.gz +OLD_FILES+=usr/share/man/man3/pmc.xscale.3.gz +OLD_FILES+=usr/share/man/man3/pmc_allocate.3.gz +OLD_FILES+=usr/share/man/man3/pmc_attach.3.gz +OLD_FILES+=usr/share/man/man3/pmc_capabilities.3.gz +OLD_FILES+=usr/share/man/man3/pmc_configure_logfile.3.gz +OLD_FILES+=usr/share/man/man3/pmc_cpuinfo.3.gz +OLD_FILES+=usr/share/man/man3/pmc_detach.3.gz +OLD_FILES+=usr/share/man/man3/pmc_disable.3.gz +OLD_FILES+=usr/share/man/man3/pmc_enable.3.gz +OLD_FILES+=usr/share/man/man3/pmc_event_names_of_class.3.gz +OLD_FILES+=usr/share/man/man3/pmc_flush_logfile.3.gz +OLD_FILES+=usr/share/man/man3/pmc_get_driver_stats.3.gz +OLD_FILES+=usr/share/man/man3/pmc_get_msr.3.gz +OLD_FILES+=usr/share/man/man3/pmc_init.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_capability.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_class.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_cputype.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_disposition.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_event.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_mode.3.gz +OLD_FILES+=usr/share/man/man3/pmc_name_of_state.3.gz +OLD_FILES+=usr/share/man/man3/pmc_ncpu.3.gz +OLD_FILES+=usr/share/man/man3/pmc_npmc.3.gz +OLD_FILES+=usr/share/man/man3/pmc_pmcinfo.3.gz +OLD_FILES+=usr/share/man/man3/pmc_read.3.gz +OLD_FILES+=usr/share/man/man3/pmc_release.3.gz +OLD_FILES+=usr/share/man/man3/pmc_rw.3.gz +OLD_FILES+=usr/share/man/man3/pmc_set.3.gz +OLD_FILES+=usr/share/man/man3/pmc_start.3.gz +OLD_FILES+=usr/share/man/man3/pmc_stop.3.gz +OLD_FILES+=usr/share/man/man3/pmc_width.3.gz +OLD_FILES+=usr/share/man/man3/pmc_write.3.gz +OLD_FILES+=usr/share/man/man3/pmc_writelog.3.gz +OLD_FILES+=usr/share/man/man3/pmclog.3.gz +OLD_FILES+=usr/share/man/man3/pmclog_close.3.gz +OLD_FILES+=usr/share/man/man3/pmclog_feed.3.gz +OLD_FILES+=usr/share/man/man3/pmclog_open.3.gz +OLD_FILES+=usr/share/man/man3/pmclog_read.3.gz +OLD_FILES+=usr/share/man/man8/pmcannotate.8.gz +OLD_FILES+=usr/share/man/man8/pmccontrol.8.gz +OLD_FILES+=usr/share/man/man8/pmcstat.8.gz +.endif + .if ${MK_PORTSNAP} == no OLD_FILES+=etc/portsnap.conf OLD_FILES+=usr/libexec/make_index OLD_FILES+=usr/libexec/phttpget OLD_FILES+=usr/sbin/portsnap OLD_FILES+=usr/share/examples/etc/portsnap.conf +OLD_FILES+=usr/share/man/man8/phttpget.8.gz OLD_FILES+=usr/share/man/man8/portsnap.8.gz .endif .if ${MK_PPP} == no OLD_FILES+=etc/ppp/ppp.conf OLD_DIRS+=etc/ppp OLD_FILES+=etc/rc.d/ppp OLD_FILES+=etc/rc.d/pppoed OLD_FILES+=etc/rc.d/sppp OLD_FILES+=sbin/spppcontrol OLD_FILES+=usr/sbin/ppp OLD_FILES+=usr/sbin/pppctl OLD_FILES+=usr/share/man/man8/ppp.8.gz OLD_FILES+=usr/share/man/man8/pppctl.8.gz OLD_FILES+=usr/share/man/man8/spppcontrol.8.gz .endif .if ${MK_PROFILE} == no OLD_FILES+=usr/lib/libalias_cuseeme_p.a OLD_FILES+=usr/lib/libalias_dummy_p.a OLD_FILES+=usr/lib/libalias_ftp_p.a OLD_FILES+=usr/lib/libalias_irc_p.a OLD_FILES+=usr/lib/libalias_nbt_p.a OLD_FILES+=usr/lib/libalias_p.a OLD_FILES+=usr/lib/libalias_pptp_p.a OLD_FILES+=usr/lib/libalias_skinny_p.a OLD_FILES+=usr/lib/libalias_smedia_p.a OLD_FILES+=usr/lib/libarchive_p.a OLD_FILES+=usr/lib/libasn1_p.a OLD_FILES+=usr/lib/libbegemot_p.a OLD_FILES+=usr/lib/libbluetooth_p.a OLD_FILES+=usr/lib/libbsdxml_p.a OLD_FILES+=usr/lib/libbsm_p.a OLD_FILES+=usr/lib/libbsnmp_p.a OLD_FILES+=usr/lib/libbz2_p.a OLD_FILES+=usr/lib/libc_p.a OLD_FILES+=usr/lib/libcalendar_p.a OLD_FILES+=usr/lib/libcam_p.a OLD_FILES+=usr/lib/libcom_err_p.a OLD_FILES+=usr/lib/libcompat_p.a OLD_FILES+=usr/lib/libcrypt_p.a OLD_FILES+=usr/lib/libcrypto_p.a OLD_FILES+=usr/lib/libcurses_p.a OLD_FILES+=usr/lib/libcursesw_p.a OLD_FILES+=usr/lib/libdevinfo_p.a OLD_FILES+=usr/lib/libdevstat_p.a OLD_FILES+=usr/lib/libdialog_p.a OLD_FILES+=usr/lib/libedit_p.a OLD_FILES+=usr/lib/libelf_p.a OLD_FILES+=usr/lib/libfetch_p.a OLD_FILES+=usr/lib/libfl_p.a OLD_FILES+=usr/lib/libform_p.a OLD_FILES+=usr/lib/libformw_p.a OLD_FILES+=usr/lib/libgcc_p.a OLD_FILES+=usr/lib/libgeom_p.a OLD_FILES+=usr/lib/libgnuregex_p.a OLD_FILES+=usr/lib/libgssapi_krb5_p.a OLD_FILES+=usr/lib/libgssapi_p.a OLD_FILES+=usr/lib/libhdb_p.a OLD_FILES+=usr/lib/libheimbase_p.a OLD_FILES+=usr/lib/libheimsqlite_p.a OLD_FILES+=usr/lib/libhistory_p.a OLD_FILES+=usr/lib/libipsec_p.a OLD_FILES+=usr/lib/libjail_p.a OLD_FILES+=usr/lib/libkadm5clnt_p.a OLD_FILES+=usr/lib/libkadm5srv_p.a OLD_FILES+=usr/lib/libkafs5_p.a OLD_FILES+=usr/lib/libkdc_p.a OLD_FILES+=usr/lib/libkiconv_p.a OLD_FILES+=usr/lib/libkrb5_p.a OLD_FILES+=usr/lib/libkvm_p.a OLD_FILES+=usr/lib/libl_p.a OLD_FILES+=usr/lib/libln_p.a OLD_FILES+=usr/lib/libm_p.a OLD_FILES+=usr/lib/libmagic_p.a OLD_FILES+=usr/lib/libmd_p.a OLD_FILES+=usr/lib/libmemstat_p.a OLD_FILES+=usr/lib/libmenu_p.a OLD_FILES+=usr/lib/libmenuw_p.a OLD_FILES+=usr/lib/libmilter_p.a OLD_FILES+=usr/lib/libmp_p.a OLD_FILES+=usr/lib/libncurses_p.a OLD_FILES+=usr/lib/libncursesw_p.a OLD_FILES+=usr/lib/libnetgraph_p.a OLD_FILES+=usr/lib/libngatm_p.a OLD_FILES+=usr/lib/libopie_p.a OLD_FILES+=usr/lib/libpanel_p.a OLD_FILES+=usr/lib/libpanelw_p.a OLD_FILES+=usr/lib/libpcap_p.a OLD_FILES+=usr/lib/libpmc_p.a OLD_FILES+=usr/lib/libpthread_p.a OLD_FILES+=usr/lib/libradius_p.a OLD_FILES+=usr/lib/libroken_p.a OLD_FILES+=usr/lib/librpcsvc_p.a OLD_FILES+=usr/lib/librt_p.a OLD_FILES+=usr/lib/libsbuf_p.a OLD_FILES+=usr/lib/libsdp_p.a OLD_FILES+=usr/lib/libsmb_p.a OLD_FILES+=usr/lib/libssl_p.a OLD_FILES+=usr/lib/libstdc++_p.a OLD_FILES+=usr/lib/libsupc++_p.a OLD_FILES+=usr/lib/libtacplus_p.a OLD_FILES+=usr/lib/libtermcap_p.a OLD_FILES+=usr/lib/libtermcapw_p.a OLD_FILES+=usr/lib/libtermlib_p.a OLD_FILES+=usr/lib/libtermlibw_p.a OLD_FILES+=usr/lib/libthr_p.a OLD_FILES+=usr/lib/libthread_db_p.a OLD_FILES+=usr/lib/libtinfo_p.a OLD_FILES+=usr/lib/libtinfow_p.a OLD_FILES+=usr/lib/libufs_p.a OLD_FILES+=usr/lib/libugidfw_p.a OLD_FILES+=usr/lib/libusbhid_p.a OLD_FILES+=usr/lib/libutil_p.a OLD_FILES+=usr/lib/libvgl_p.a OLD_FILES+=usr/lib/libwind_p.a OLD_FILES+=usr/lib/libwrap_p.a OLD_FILES+=usr/lib/liby_p.a OLD_FILES+=usr/lib/libypclnt_p.a OLD_FILES+=usr/lib/libz_p.a OLD_FILES+=usr/lib/private/libldns_p.a OLD_FILES+=usr/lib/private/libssh_p.a .endif -.if ${MK_RADIUS_SUPPORT} == no -OLD_FILES+=usr/include/radlib.h -OLD_FILES+=usr/include/radlib_vs.h -OLD_FILES+=usr/lib/libradius.a -OLD_LIBS+=usr/lib/libradius.so.4 -OLD_LIBS+=usr/lib/pam_radius.so.5 -OLD_FILES+=usr/lib/libradius_p.a -OLD_FILES+=usr/share/man/man3/libradius.3.gz -OLD_FILES+=usr/share/man/man3/rad_acct_open.3.gz -OLD_FILES+=usr/share/man/man3/rad_add_server.3.gz -OLD_FILES+=usr/share/man/man3/rad_add_server_ex.3.gz -OLD_FILES+=usr/share/man/man3/rad_auth_open.3.gz -OLD_FILES+=usr/share/man/man3/rad_bind_to.3.gz -OLD_FILES+=usr/share/man/man3/rad_close.3.gz -OLD_FILES+=usr/share/man/man3/rad_config.3.gz -OLD_FILES+=usr/share/man/man3/rad_continue_send_request.3.gz -OLD_FILES+=usr/share/man/man3/rad_create_request.3.gz -OLD_FILES+=usr/share/man/man3/rad_create_response.3.gz -OLD_FILES+=usr/share/man/man3/rad_cvt_addr.3.gz -OLD_FILES+=usr/share/man/man3/rad_cvt_int.3.gz -OLD_FILES+=usr/share/man/man3/rad_cvt_string.3.gz -OLD_FILES+=usr/share/man/man3/rad_demangle.3.gz -OLD_FILES+=usr/share/man/man3/rad_demangle_mppe_key.3.gz -OLD_FILES+=usr/share/man/man3/rad_get_attr.3.gz -OLD_FILES+=usr/share/man/man3/rad_get_vendor_attr.3.gz -OLD_FILES+=usr/share/man/man3/rad_init_send_request.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_addr.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_attr.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_int.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_message_authentic.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_string.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_vendor_addr.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_vendor_attr.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_vendor_int.3.gz -OLD_FILES+=usr/share/man/man3/rad_put_vendor_string.3.gz -OLD_FILES+=usr/share/man/man3/rad_receive_request.3.gz -OLD_FILES+=usr/share/man/man3/rad_request_authenticator.3.gz -OLD_FILES+=usr/share/man/man3/rad_send_request.3.gz -OLD_FILES+=usr/share/man/man3/rad_send_response.3.gz -OLD_FILES+=usr/share/man/man3/rad_server_open.3.gz -OLD_FILES+=usr/share/man/man3/rad_server_secret.3.gz -OLD_FILES+=usr/share/man/man3/rad_strerror.3.gz -OLD_FILES+=usr/share/man/man5/radius.conf.5.gz -OLD_FILES+=usr/share/man/man8/pam_radius.8.gz +.if ${MK_QUOTAS} == no +OLD_FILES+=sbin/quotacheck +OLD_FILES+=usr/bin/quota +OLD_FILES+=usr/sbin/edquota +OLD_FILES+=usr/sbin/quotaoff +OLD_FILES+=usr/sbin/quotaon +OLD_FILES+=usr/sbin/repquota +OLD_FILES+=usr/share/man/man1/quota.1.gz +OLD_FILES+=usr/share/man/man8/edquota.8.gz +OLD_FILES+=usr/share/man/man8/quotacheck.8.gz +OLD_FILES+=usr/share/man/man8/quotaoff.8.gz +OLD_FILES+=usr/share/man/man8/quotaon.8.gz +OLD_FILES+=usr/share/man/man8/repquota.8.gz .endif -.if ${MK_RBOOTD} == no -OLD_FILES+=usr/libexec/rbootd -OLD_FILES+=usr/share/man/man8/rbootd.8.gz -.endif - .if ${MK_RCMDS} == no OLD_FILES+=usr/include/protocols/rwhod.h OLD_FILES+=bin/rcp OLD_FILES+=etc/pam.d/rsh OLD_FILES+=etc/rc.d/rwho OLD_FILES+=etc/periodic/daily/140.clean-rwho OLD_FILES+=etc/periodic/daily/430.status-rwho OLD_FILES+=rescue/rcp OLD_FILES+=usr/bin/rlogin OLD_FILES+=usr/bin/rsh OLD_FILES+=usr/bin/ruptime OLD_FILES+=usr/bin/rwho OLD_FILES+=usr/libexec/rlogind OLD_FILES+=usr/libexec/rshd OLD_FILES+=usr/sbin/rwhod OLD_FILES+=usr/share/man/man1/rcp.1.gz OLD_FILES+=usr/share/man/man1/rlogin.1.gz OLD_FILES+=usr/share/man/man1/rsh.1.gz OLD_FILES+=usr/share/man/man1/ruptime.1.gz OLD_FILES+=usr/share/man/man1/rwho.1.gz OLD_FILES+=usr/share/man/man8/rlogind.8.gz OLD_FILES+=usr/share/man/man8/rshd.8.gz OLD_FILES+=usr/share/man/man8/rwhod.8.gz .endif .if ${MK_RCS} == no OLD_FILES+=usr/bin/ci OLD_FILES+=usr/bin/co OLD_FILES+=usr/bin/ident OLD_FILES+=usr/bin/merge OLD_FILES+=usr/bin/rcs OLD_FILES+=usr/bin/rcsclean OLD_FILES+=usr/bin/rcsdiff OLD_FILES+=usr/bin/rcsfreeze OLD_FILES+=usr/bin/rcsmerge OLD_FILES+=usr/bin/rlog OLD_FILES+=usr/sbin/etcupdate OLD_FILES+=usr/share/man/man1/ci.1.gz OLD_FILES+=usr/share/man/man1/co.1.gz OLD_FILES+=usr/share/man/man1/ident.1.gz OLD_FILES+=usr/share/man/man1/merge.1.gz OLD_FILES+=usr/share/man/man1/rcs.1.gz OLD_FILES+=usr/share/man/man1/rcsclean.1.gz OLD_FILES+=usr/share/man/man1/rcsdiff.1.gz OLD_FILES+=usr/share/man/man1/rcsfreeze.1.gz OLD_FILES+=usr/share/man/man1/rcsintro.1.gz OLD_FILES+=usr/share/man/man1/rcsmerge.1.gz OLD_FILES+=usr/share/man/man1/rlog.1.gz OLD_FILES+=usr/share/man/man5/rcsfile.5.gz OLD_FILES+=usr/share/man/man8/etcupdate.8.gz .endif #.if ${MK_RESCUE} == no # to be filled in or replaced with a special target #.endif .if ${MK_ROUTED} == no OLD_FILES+=etc/rc.d/routed OLD_FILES+=usr/include/protocols/routed.h OLD_FILES+=sbin/routed OLD_FILES+=sbin/rtquery OLD_FILES+=usr/share/man/man8/routed.8.gz OLD_FILES+=usr/share/man/man8/rtquery.8.gz .endif .if ${MK_SENDMAIL} == no OLD_FILES+=etc/periodic/daily/150.clean-hoststat OLD_FILES+=etc/periodic/daily/440.status-mailq OLD_FILES+=etc/periodic/daily/460.status-mail-rejects OLD_FILES+=etc/periodic/daily/500.queuerun OLD_FILES+=etc/rc.sendmail OLD_FILES+=etc/rc.d/sendmail .if ${MK_MAILWRAPPER} == no OLD_FILES+=bin/rmail .endif OLD_FILES+=usr/bin/vacation OLD_FILES+=usr/include/libmilter/mfapi.h OLD_FILES+=usr/include/libmilter/mfdef.h OLD_DIRS+=usr/include/libmilter OLD_FILES+=usr/lib/libmilter.a OLD_FILES+=usr/lib/libmilter.so OLD_LIBS+=usr/lib/libmilter.so.5 OLD_FILES+=usr/lib/libmilter_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libmilter.a OLD_FILES+=usr/lib32/libmilter.so OLD_LIBS+=usr/lib32/libmilter.so.5 OLD_FILES+=usr/lib32/libmilter_p.a .endif OLD_FILES+=usr/libexec/mail.local OLD_FILES+=usr/libexec/sendmail/sendmail OLD_FILES+=usr/libexec/smrsh OLD_FILES+=usr/sbin/editmap OLD_FILES+=usr/sbin/mailstats OLD_FILES+=usr/sbin/makemap OLD_FILES+=usr/sbin/praliases OLD_FILES+=usr/share/doc/smm/08.sendmailop/paper.ascii.gz OLD_DIRS+=usr/share/doc/smm/08.sendmailop OLD_FILES+=usr/share/man/man1/mailq.1.gz OLD_FILES+=usr/share/man/man1/newaliases.1.gz OLD_FILES+=usr/share/man/man1/vacation.1.gz OLD_FILES+=usr/share/man/man5/aliases.5.gz OLD_FILES+=usr/share/man/man8/editmap.8.gz OLD_FILES+=usr/share/man/man8/hoststat.8.gz OLD_FILES+=usr/share/man/man8/mail.local.8.gz OLD_FILES+=usr/share/man/man8/mailstats.8.gz OLD_FILES+=usr/share/man/man8/makemap.8.gz OLD_FILES+=usr/share/man/man8/praliases.8.gz OLD_FILES+=usr/share/man/man8/purgestat.8.gz OLD_FILES+=usr/share/man/man8/rmail.8.gz OLD_FILES+=usr/share/man/man8/sendmail.8.gz OLD_FILES+=usr/share/man/man8/smrsh.8.gz OLD_FILES+=usr/share/sendmail/cf/README OLD_FILES+=usr/share/sendmail/cf/cf/Makefile OLD_FILES+=usr/share/sendmail/cf/cf/README OLD_FILES+=usr/share/sendmail/cf/cf/chez.cs.mc OLD_FILES+=usr/share/sendmail/cf/cf/clientproto.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-hpux10.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-hpux9.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-osf1.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-solaris2.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-sunos4.1.mc OLD_FILES+=usr/share/sendmail/cf/cf/cs-ultrix4.mc OLD_FILES+=usr/share/sendmail/cf/cf/cyrusproto.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-bsd4.4.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-hpux10.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-hpux9.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-linux.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-mpeix.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-nextstep3.3.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-osf1.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-solaris.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-sunos4.1.mc OLD_FILES+=usr/share/sendmail/cf/cf/generic-ultrix4.mc OLD_FILES+=usr/share/sendmail/cf/cf/huginn.cs.mc OLD_FILES+=usr/share/sendmail/cf/cf/knecht.mc OLD_FILES+=usr/share/sendmail/cf/cf/mail.cs.mc OLD_FILES+=usr/share/sendmail/cf/cf/mail.eecs.mc OLD_FILES+=usr/share/sendmail/cf/cf/mailspool.cs.mc OLD_FILES+=usr/share/sendmail/cf/cf/python.cs.mc OLD_FILES+=usr/share/sendmail/cf/cf/s2k-osf1.mc OLD_FILES+=usr/share/sendmail/cf/cf/s2k-ultrix4.mc OLD_FILES+=usr/share/sendmail/cf/cf/submit.cf OLD_FILES+=usr/share/sendmail/cf/cf/submit.mc OLD_FILES+=usr/share/sendmail/cf/cf/tcpproto.mc OLD_FILES+=usr/share/sendmail/cf/cf/ucbarpa.mc OLD_FILES+=usr/share/sendmail/cf/cf/ucbvax.mc OLD_FILES+=usr/share/sendmail/cf/cf/uucpproto.mc OLD_FILES+=usr/share/sendmail/cf/cf/vangogh.cs.mc OLD_DIRS+=usr/share/sendmail/cf/cf OLD_FILES+=usr/share/sendmail/cf/domain/Berkeley.EDU.m4 OLD_FILES+=usr/share/sendmail/cf/domain/CS.Berkeley.EDU.m4 OLD_FILES+=usr/share/sendmail/cf/domain/EECS.Berkeley.EDU.m4 OLD_FILES+=usr/share/sendmail/cf/domain/S2K.Berkeley.EDU.m4 OLD_FILES+=usr/share/sendmail/cf/domain/berkeley-only.m4 OLD_FILES+=usr/share/sendmail/cf/domain/generic.m4 OLD_DIRS+=usr/share/sendmail/cf/domain OLD_FILES+=usr/share/sendmail/cf/feature/accept_unqualified_senders.m4 OLD_FILES+=usr/share/sendmail/cf/feature/accept_unresolvable_domains.m4 OLD_FILES+=usr/share/sendmail/cf/feature/access_db.m4 OLD_FILES+=usr/share/sendmail/cf/feature/allmasquerade.m4 OLD_FILES+=usr/share/sendmail/cf/feature/always_add_domain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/authinfo.m4 OLD_FILES+=usr/share/sendmail/cf/feature/badmx.m4 OLD_FILES+=usr/share/sendmail/cf/feature/bestmx_is_local.m4 OLD_FILES+=usr/share/sendmail/cf/feature/bitdomain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/blacklist_recipients.m4 OLD_FILES+=usr/share/sendmail/cf/feature/block_bad_helo.m4 OLD_FILES+=usr/share/sendmail/cf/feature/compat_check.m4 OLD_FILES+=usr/share/sendmail/cf/feature/conncontrol.m4 OLD_FILES+=usr/share/sendmail/cf/feature/delay_checks.m4 OLD_FILES+=usr/share/sendmail/cf/feature/dnsbl.m4 OLD_FILES+=usr/share/sendmail/cf/feature/domaintable.m4 OLD_FILES+=usr/share/sendmail/cf/feature/enhdnsbl.m4 OLD_FILES+=usr/share/sendmail/cf/feature/generics_entire_domain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/genericstable.m4 OLD_FILES+=usr/share/sendmail/cf/feature/greet_pause.m4 OLD_FILES+=usr/share/sendmail/cf/feature/ldap_routing.m4 OLD_FILES+=usr/share/sendmail/cf/feature/limited_masquerade.m4 OLD_FILES+=usr/share/sendmail/cf/feature/local_lmtp.m4 OLD_FILES+=usr/share/sendmail/cf/feature/local_no_masquerade.m4 OLD_FILES+=usr/share/sendmail/cf/feature/local_procmail.m4 OLD_FILES+=usr/share/sendmail/cf/feature/lookupdotdomain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/loose_relay_check.m4 OLD_FILES+=usr/share/sendmail/cf/feature/mailertable.m4 OLD_FILES+=usr/share/sendmail/cf/feature/masquerade_entire_domain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/masquerade_envelope.m4 OLD_FILES+=usr/share/sendmail/cf/feature/msp.m4 OLD_FILES+=usr/share/sendmail/cf/feature/mtamark.m4 OLD_FILES+=usr/share/sendmail/cf/feature/no_default_msa.m4 OLD_FILES+=usr/share/sendmail/cf/feature/nocanonify.m4 OLD_FILES+=usr/share/sendmail/cf/feature/notsticky.m4 OLD_FILES+=usr/share/sendmail/cf/feature/nouucp.m4 OLD_FILES+=usr/share/sendmail/cf/feature/nullclient.m4 OLD_FILES+=usr/share/sendmail/cf/feature/preserve_local_plus_detail.m4 OLD_FILES+=usr/share/sendmail/cf/feature/preserve_luser_host.m4 OLD_FILES+=usr/share/sendmail/cf/feature/promiscuous_relay.m4 OLD_FILES+=usr/share/sendmail/cf/feature/queuegroup.m4 OLD_FILES+=usr/share/sendmail/cf/feature/ratecontrol.m4 OLD_FILES+=usr/share/sendmail/cf/feature/redirect.m4 OLD_FILES+=usr/share/sendmail/cf/feature/relay_based_on_MX.m4 OLD_FILES+=usr/share/sendmail/cf/feature/relay_entire_domain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/relay_hosts_only.m4 OLD_FILES+=usr/share/sendmail/cf/feature/relay_local_from.m4 OLD_FILES+=usr/share/sendmail/cf/feature/relay_mail_from.m4 OLD_FILES+=usr/share/sendmail/cf/feature/require_rdns.m4 OLD_FILES+=usr/share/sendmail/cf/feature/smrsh.m4 OLD_FILES+=usr/share/sendmail/cf/feature/stickyhost.m4 OLD_FILES+=usr/share/sendmail/cf/feature/use_client_ptr.m4 OLD_FILES+=usr/share/sendmail/cf/feature/use_ct_file.m4 OLD_FILES+=usr/share/sendmail/cf/feature/use_cw_file.m4 OLD_FILES+=usr/share/sendmail/cf/feature/uucpdomain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/virtuser_entire_domain.m4 OLD_FILES+=usr/share/sendmail/cf/feature/virtusertable.m4 OLD_DIRS+=usr/share/sendmail/cf/feature OLD_FILES+=usr/share/sendmail/cf/hack/cssubdomain.m4 OLD_DIRS+=usr/share/sendmail/cf/hack OLD_FILES+=usr/share/sendmail/cf/m4/cf.m4 OLD_FILES+=usr/share/sendmail/cf/m4/cfhead.m4 OLD_FILES+=usr/share/sendmail/cf/m4/proto.m4 OLD_FILES+=usr/share/sendmail/cf/m4/version.m4 OLD_DIRS+=usr/share/sendmail/cf/m4 OLD_FILES+=usr/share/sendmail/cf/mailer/cyrus.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/cyrusv2.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/fax.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/local.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/mail11.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/phquery.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/pop.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/procmail.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/qpage.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/smtp.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/usenet.m4 OLD_FILES+=usr/share/sendmail/cf/mailer/uucp.m4 OLD_DIRS+=usr/share/sendmail/cf/mailer OLD_FILES+=usr/share/sendmail/cf/ostype/a-ux.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/aix3.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/aix4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/aix5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/altos.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/amdahl-uts.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/bsd4.3.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/bsd4.4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/bsdi.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/bsdi1.0.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/bsdi2.0.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/darwin.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/dgux.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/domainos.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/dragonfly.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/dynix3.2.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/freebsd4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/freebsd5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/freebsd6.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/gnu.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/hpux10.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/hpux11.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/hpux9.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/irix4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/irix5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/irix6.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/isc4.1.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/linux.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/maxion.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/mklinux.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/mpeix.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/nextstep.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/openbsd.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/osf1.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/powerux.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/ptx2.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/qnx.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/riscos4.5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/sco-uw-2.1.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/sco3.2.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/sinix.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/solaris11.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/solaris2.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/solaris2.ml.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/solaris2.pre5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/solaris8.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/sunos3.5.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/sunos4.1.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/svr4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/ultrix4.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/unicos.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/unicosmk.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/unicosmp.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/unixware7.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/unknown.m4 OLD_FILES+=usr/share/sendmail/cf/ostype/uxpds.m4 OLD_DIRS+=usr/share/sendmail/cf/ostype OLD_FILES+=usr/share/sendmail/cf/sendmail.schema OLD_FILES+=usr/share/sendmail/cf/sh/makeinfo.sh OLD_DIRS+=usr/share/sendmail/cf/sh OLD_FILES+=usr/share/sendmail/cf/siteconfig/uucp.cogsci.m4 OLD_FILES+=usr/share/sendmail/cf/siteconfig/uucp.old.arpa.m4 OLD_FILES+=usr/share/sendmail/cf/siteconfig/uucp.ucbarpa.m4 OLD_FILES+=usr/share/sendmail/cf/siteconfig/uucp.ucbvax.m4 OLD_DIRS+=usr/share/sendmail/cf/siteconfig OLD_DIRS+=usr/share/sendmail/cf OLD_DIRS+=usr/share/sendmail .endif .if ${MK_SHAREDOCS} == no OLD_FILES+=usr/share/doc/pjdfstest/README OLD_DIRS+=usr/share/doc/pjdfstest .endif -.if ${MK_SQLITE3} == no -OLD_LIBS+=usr/lib/private/libsqlite3.so.0 +.if ${MK_SSP} == no +OLD_LIBS+=lib/libssp.so.0 +OLD_FILES+=usr/include/ssp/ssp.h +OLD_FILES+=usr/include/ssp/stdio.h +OLD_FILES+=usr/include/ssp/string.h +OLD_FILES+=usr/include/ssp/unistd.h +OLD_FILES+=usr/lib/libssp.a +OLD_FILES+=usr/lib/libssp.so +OLD_FILES+=usr/lib/libssp_nonshared.a +OLD_FILES+=usr/lib32/libssp.a +OLD_FILES+=usr/lib32/libssp.so +OLD_LIBS+=usr/lib32/libssp.so.0 +OLD_FILES+=usr/lib32/libssp_nonshared.a +OLD_FILES+=usr/tests/lib/libc/ssp/Kyuafile +OLD_FILES+=usr/tests/lib/libc/ssp/h_fgets +OLD_FILES+=usr/tests/lib/libc/ssp/h_getcwd +OLD_FILES+=usr/tests/lib/libc/ssp/h_gets +OLD_FILES+=usr/tests/lib/libc/ssp/h_memcpy +OLD_FILES+=usr/tests/lib/libc/ssp/h_memmove +OLD_FILES+=usr/tests/lib/libc/ssp/h_memset +OLD_FILES+=usr/tests/lib/libc/ssp/h_read +OLD_FILES+=usr/tests/lib/libc/ssp/h_readlink +OLD_FILES+=usr/tests/lib/libc/ssp/h_snprintf +OLD_FILES+=usr/tests/lib/libc/ssp/h_sprintf +OLD_FILES+=usr/tests/lib/libc/ssp/h_stpcpy +OLD_FILES+=usr/tests/lib/libc/ssp/h_stpncpy +OLD_FILES+=usr/tests/lib/libc/ssp/h_strcat +OLD_FILES+=usr/tests/lib/libc/ssp/h_strcpy +OLD_FILES+=usr/tests/lib/libc/ssp/h_strncat +OLD_FILES+=usr/tests/lib/libc/ssp/h_strncpy +OLD_FILES+=usr/tests/lib/libc/ssp/h_vsnprintf +OLD_FILES+=usr/tests/lib/libc/ssp/h_vsprintf +OLD_FILES+=usr/tests/lib/libc/ssp/ssp_test .endif .if ${MK_SYSCONS} == no -OLD_FILES+=usr/share/man/man4/syscons.4.gz OLD_FILES+=usr/share/syscons/fonts/INDEX.fonts OLD_FILES+=usr/share/syscons/fonts/armscii8-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/armscii8-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/armscii8-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp1251-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp1251-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp1251-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp437-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp437-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp437-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp437-thin-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp437-thin-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp850-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp850-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp850-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp850-thin-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp850-thin-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp865-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp865-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp865-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp865-thin-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp865-thin-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp866-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp866-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp866-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/cp866b-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp866c-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp866u-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/cp866u-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/cp866u-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/haik8-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/haik8-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/haik8-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso-thin-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso02-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso02-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso02-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-vga9-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-vga9-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-vga9-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-vga9-wide-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso04-wide-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso05-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso05-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso05-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso07-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso07-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso07-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso08-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso08-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso08-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso09-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso15-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/iso15-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/iso15-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/iso15-thin-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-r-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-r-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-r-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-rb-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-rc-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-u-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-u-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/koi8-u-8x8.fnt OLD_FILES+=usr/share/syscons/fonts/swiss-1131-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/swiss-1251-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/swiss-8x14.fnt OLD_FILES+=usr/share/syscons/fonts/swiss-8x16.fnt OLD_FILES+=usr/share/syscons/fonts/swiss-8x8.fnt OLD_FILES+=usr/share/syscons/keymaps/INDEX.keymaps OLD_FILES+=usr/share/syscons/keymaps/be.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/be.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/bg.bds.ctrlcaps.kbd OLD_FILES+=usr/share/syscons/keymaps/bg.phonetic.ctrlcaps.kbd OLD_FILES+=usr/share/syscons/keymaps/br275.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/br275.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/br275.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/by.cp1131.kbd OLD_FILES+=usr/share/syscons/keymaps/by.cp1251.kbd OLD_FILES+=usr/share/syscons/keymaps/by.iso5.kbd OLD_FILES+=usr/share/syscons/keymaps/ce.iso2.kbd OLD_FILES+=usr/share/syscons/keymaps/colemak.iso15.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/cs.latin2.qwertz.kbd OLD_FILES+=usr/share/syscons/keymaps/cz.iso2.kbd OLD_FILES+=usr/share/syscons/keymaps/danish.cp865.kbd OLD_FILES+=usr/share/syscons/keymaps/danish.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/danish.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/danish.iso.macbook.kbd OLD_FILES+=usr/share/syscons/keymaps/dutch.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/eee_nordic.kbd OLD_FILES+=usr/share/syscons/keymaps/el.iso07.kbd OLD_FILES+=usr/share/syscons/keymaps/estonian.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/estonian.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/estonian.iso15.kbd OLD_FILES+=usr/share/syscons/keymaps/finnish.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/finnish.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/fr.dvorak.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/fr.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/fr.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/fr.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/fr.macbook.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/fr_CA.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/german.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/german.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/german.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/gr.elot.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/gr.us101.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/hr.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/hu.iso2.101keys.kbd OLD_FILES+=usr/share/syscons/keymaps/hu.iso2.102keys.kbd OLD_FILES+=usr/share/syscons/keymaps/hy.armscii-8.kbd OLD_FILES+=usr/share/syscons/keymaps/icelandic.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/icelandic.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/it.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/iw.iso8.kbd OLD_FILES+=usr/share/syscons/keymaps/jp.106.kbd OLD_FILES+=usr/share/syscons/keymaps/jp.106x.kbd OLD_FILES+=usr/share/syscons/keymaps/jp.pc98.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/jp.pc98.kbd OLD_FILES+=usr/share/syscons/keymaps/kk.pt154.io.kbd OLD_FILES+=usr/share/syscons/keymaps/kk.pt154.kst.kbd OLD_FILES+=usr/share/syscons/keymaps/latinamerican.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/latinamerican.kbd OLD_FILES+=usr/share/syscons/keymaps/lt.iso4.kbd OLD_FILES+=usr/share/syscons/keymaps/norwegian.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/norwegian.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/pl_PL.ISO8859-2.kbd OLD_FILES+=usr/share/syscons/keymaps/pl_PL.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/pt.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/pt.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/ru.cp866.kbd OLD_FILES+=usr/share/syscons/keymaps/ru.iso5.kbd OLD_FILES+=usr/share/syscons/keymaps/ru.koi8-r.kbd OLD_FILES+=usr/share/syscons/keymaps/ru.koi8-r.shift.kbd OLD_FILES+=usr/share/syscons/keymaps/ru.koi8-r.win.kbd OLD_FILES+=usr/share/syscons/keymaps/si.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/sk.iso2.kbd OLD_FILES+=usr/share/syscons/keymaps/spanish.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/spanish.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/spanish.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/spanish.iso15.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/swedish.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/swedish.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/swissfrench.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/swissfrench.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/swissfrench.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/swissgerman.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/swissgerman.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/swissgerman.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/swissgerman.macbook.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/tr.iso9.q.kbd OLD_FILES+=usr/share/syscons/keymaps/ua.iso5.kbd OLD_FILES+=usr/share/syscons/keymaps/ua.koi8-u.kbd OLD_FILES+=usr/share/syscons/keymaps/ua.koi8-u.shift.alt.kbd OLD_FILES+=usr/share/syscons/keymaps/uk.cp850-ctrl.kbd OLD_FILES+=usr/share/syscons/keymaps/uk.cp850.kbd OLD_FILES+=usr/share/syscons/keymaps/uk.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/uk.iso-ctrl.kbd OLD_FILES+=usr/share/syscons/keymaps/uk.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/us.dvorak.kbd OLD_FILES+=usr/share/syscons/keymaps/us.dvorakl.kbd OLD_FILES+=usr/share/syscons/keymaps/us.dvorakp.kbd OLD_FILES+=usr/share/syscons/keymaps/us.dvorakr.kbd OLD_FILES+=usr/share/syscons/keymaps/us.dvorakx.kbd OLD_FILES+=usr/share/syscons/keymaps/us.emacs.kbd OLD_FILES+=usr/share/syscons/keymaps/us.iso.acc.kbd OLD_FILES+=usr/share/syscons/keymaps/us.iso.kbd OLD_FILES+=usr/share/syscons/keymaps/us.pc-ctrl.kbd OLD_FILES+=usr/share/syscons/keymaps/us.unix.kbd OLD_FILES+=usr/share/syscons/scrnmaps/armscii8-2haik8.scm OLD_FILES+=usr/share/syscons/scrnmaps/iso-8859-1_to_cp437.scm OLD_FILES+=usr/share/syscons/scrnmaps/iso-8859-4_for_vga9.scm OLD_FILES+=usr/share/syscons/scrnmaps/iso-8859-7_to_cp437.scm OLD_FILES+=usr/share/syscons/scrnmaps/koi8-r2cp866.scm OLD_FILES+=usr/share/syscons/scrnmaps/koi8-u2cp866u.scm OLD_FILES+=usr/share/syscons/scrnmaps/us-ascii_to_cp437.scm -OLD_DIRS+=usr/share/syscons/fonts -OLD_DIRS+=usr/share/syscons/keymaps -OLD_DIRS+=usr/share/syscons/scrnmaps -OLD_DIRS+=usr/share/syscons .endif .if ${MK_TALK} == no OLD_FILES+=usr/bin/talk OLD_FILES+=usr/include/protocols/talkd.h OLD_FILES+=usr/libexec/ntalkd OLD_FILES+=usr/share/man/man1/talk.1.gz OLD_FILES+=usr/share/man/man8/talkd.8.gz .endif .if ${MK_TCP_WRAPPERS} == no OLD_FILES+=usr/libexec/tcpd OLD_FILES+=usr/sbin/tcpdchk OLD_FILES+=usr/sbin/tcpdmatch OLD_FILES+=usr/share/man/man8/tcpd.8.gz OLD_FILES+=usr/share/man/man8/tcpdchk.8.gz OLD_FILES+=usr/share/man/man8/tcpdmatch.8.gz .endif .if ${MK_TCSH} == no +OLD_FILES+=.cshrc +OLD_FILES+=etc/csh.cshrc +OLD_FILES+=etc/csh.login +OLD_FILES+=etc/csh.logout OLD_FILES+=bin/csh OLD_FILES+=bin/tcsh OLD_FILES+=rescue/csh OLD_FILES+=rescue/tcsh +OLD_FILES+=root/.cshrc +OLD_FILES+=root/.login +OLD_FILES+=usr/share/examples/etc/csh.cshrc +OLD_FILES+=usr/share/examples/etc/csh.login +OLD_FILES+=usr/share/examples/etc/csh.logout OLD_FILES+=usr/share/examples/tcsh/complete.tcsh OLD_FILES+=usr/share/examples/tcsh/csh-mode.el OLD_DIRS+=usr/share/examples/tcsh OLD_FILES+=usr/share/man/man1/csh.1.gz OLD_FILES+=usr/share/man/man1/tcsh.1.gz OLD_FILES+=usr/share/nls/de_AT.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/de_AT.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/de_AT.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/de_CH.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/de_CH.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/de_CH.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/de_DE.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/de_DE.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/de_DE.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/el_GR.ISO8859-7/tcsh.cat OLD_FILES+=usr/share/nls/el_GR.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/es_ES.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/es_ES.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/es_ES.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/et_EE.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/et_EE.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/fi_FI.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/fi_FI.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/fi_FI.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/fr_BE.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/fr_BE.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/fr_BE.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/fr_CA.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/fr_CA.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/fr_CA.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/fr_CH.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/fr_CH.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/fr_CH.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/fr_FR.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/fr_FR.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/fr_FR.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/it_CH.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/it_CH.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/it_CH.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/it_IT.ISO8859-1/tcsh.cat OLD_FILES+=usr/share/nls/it_IT.ISO8859-15/tcsh.cat OLD_FILES+=usr/share/nls/it_IT.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/ja_JP.SJIS/tcsh.cat OLD_FILES+=usr/share/nls/ja_JP.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/ja_JP.eucJP/tcsh.cat OLD_FILES+=usr/share/nls/ru_RU.CP1251/tcsh.cat OLD_FILES+=usr/share/nls/ru_RU.CP866/tcsh.cat OLD_FILES+=usr/share/nls/ru_RU.ISO8859-5/tcsh.cat OLD_FILES+=usr/share/nls/ru_RU.KOI8-R/tcsh.cat OLD_FILES+=usr/share/nls/ru_RU.UTF-8/tcsh.cat OLD_FILES+=usr/share/nls/uk_UA.ISO8859-5/tcsh.cat OLD_FILES+=usr/share/nls/uk_UA.KOI8-U/tcsh.cat OLD_FILES+=usr/share/nls/uk_UA.UTF-8/tcsh.cat .endif .if ${MK_TELNET} == no OLD_FILES+=etc/pam.d/telnetd OLD_FILES+=usr/bin/telnet OLD_FILES+=usr/libexec/telnetd OLD_FILES+=usr/share/man/man1/telnet.1.gz OLD_FILES+=usr/share/man/man8/telnetd.8.gz .endif .if ${MK_TFTP} == no OLD_FILES+=usr/bin/tftp OLD_FILES+=usr/libexec/tftpd OLD_FILES+=usr/share/man/man1/tftp.1.gz OLD_FILES+=usr/share/man/man8/tftpd.8.gz .endif .if ${MK_TESTS} == yes OLD_FILES+=usr/bin/atf-sh OLD_FILES+=usr/include/atf-c++/config.hpp OLD_FILES+=usr/include/atf-c/config.h OLD_FILES+=usr/lib/libatf-c++.a OLD_LIBS+=usr/lib/libatf-c++.so OLD_LIBS+=usr/lib/libatf-c++.so.1 OLD_LIBS+=usr/lib/libatf-c++.so.2 OLD_FILES+=usr/lib/libatf-c++_p.a OLD_FILES+=usr/lib/libatf-c.a OLD_LIBS+=usr/lib/libatf-c.so OLD_LIBS+=usr/lib/libatf-c.so.1 OLD_FILES+=usr/lib/libatf-c_p.a OLD_LIBS+=usr/lib/private/libatf-c.so.0 OLD_LIBS+=usr/lib/private/libatf-c++.so.1 .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_LIBS+=usr/lib32/libatf-c++.a OLD_LIBS+=usr/lib32/libatf-c++.so OLD_LIBS+=usr/lib32/libatf-c++.so.1 OLD_LIBS+=usr/lib32/libatf-c++.so.2 OLD_LIBS+=usr/lib32/libatf-c++_p.a OLD_LIBS+=usr/lib32/libatf-c.a OLD_LIBS+=usr/lib32/libatf-c.so OLD_LIBS+=usr/lib32/libatf-c.so.1 OLD_LIBS+=usr/lib32/libatf-c_p.a OLD_LIBS+=usr/lib32/private/libatf-c.so.0 OLD_LIBS+=usr/lib32/private/libatf-c++.so.1 .endif OLD_FILES+=usr/libdata/pkgconfig/atf-c++.pc OLD_FILES+=usr/libdata/pkgconfig/atf-c.pc OLD_FILES+=usr/libdata/pkgconfig/atf-sh.pc OLD_FILES+=usr/share/aclocal/atf-c++.m4 OLD_FILES+=usr/share/aclocal/atf-c.m4 OLD_FILES+=usr/share/aclocal/atf-common.m4 OLD_FILES+=usr/share/aclocal/atf-sh.m4 OLD_DIRS+=usr/share/aclocal OLD_FILES+=usr/tests/bin/chown/units_basics OLD_FILES+=usr/tests/bin/date/legacy_test OLD_FILES+=usr/tests/bin/sh/legacy_test OLD_FILES+=usr/tests/usr.bin/atf/Kyuafile OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/Kyuafile OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/atf_check_test OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/config_test OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/integration_test OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/misc_helpers OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/normalize_test OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/tc_test OLD_FILES+=usr/tests/usr.bin/atf/atf-sh/tp_test OLD_DIRS+=usr/tests/usr.bin/atf/atf-sh OLD_DIRS+=usr/tests/usr.bin/atf OLD_FILES+=usr/tests/lib/atf/libatf-c/test_helpers_test OLD_FILES+=usr/tests/lib/atf/test-programs/fork_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/application_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/config_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/expand_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/parser_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/sanity_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/detail/ui_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/env_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/exceptions_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/expand_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/fs_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/parser_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/process_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/sanity_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/pkg_config_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/text_test OLD_FILES+=usr/tests/lib/atf/libatf-c++/ui_test OLD_FILES+=usr/tests/lib/atf/libatf-c/config_test OLD_FILES+=usr/tests/lib/atf/libatf-c/dynstr_test OLD_FILES+=usr/tests/lib/atf/libatf-c/env_test OLD_FILES+=usr/tests/lib/atf/libatf-c/fs_test OLD_FILES+=usr/tests/lib/atf/libatf-c/list_test OLD_FILES+=usr/tests/lib/atf/libatf-c/map_test OLD_FILES+=usr/tests/lib/atf/libatf-c/pkg_config_test OLD_FILES+=usr/tests/lib/atf/libatf-c/process_helpers OLD_FILES+=usr/tests/lib/atf/libatf-c/process_test OLD_FILES+=usr/tests/lib/atf/libatf-c/sanity_test OLD_FILES+=usr/tests/lib/atf/libatf-c/text_test OLD_FILES+=usr/tests/lib/atf/libatf-c/user_test .if ${MK_MAKE} == yes OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/legacy_test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.status.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stderr.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/expected.stdout.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd/libtest.a OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/legacy_test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.status.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stderr.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/expected.stdout.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod/libtest.a OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/legacy_test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.status.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stderr.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.3 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.4 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.5 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.6 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/expected.stdout.7 OLD_FILES+=usr/tests/usr.bin/make/archives/fmt_oldbsd/libtest.a OLD_FILES+=usr/tests/usr.bin/make/archives/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/basic/t0/legacy_test OLD_FILES+=usr/tests/usr.bin/make/basic/t0/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/basic/t0/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t0/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t0/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t1/legacy_test OLD_FILES+=usr/tests/usr.bin/make/basic/t1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/basic/t1/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/basic/t1/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t1/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t1/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t2/legacy_test OLD_FILES+=usr/tests/usr.bin/make/basic/t2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/basic/t2/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/basic/t2/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t2/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t2/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t3/legacy_test OLD_FILES+=usr/tests/usr.bin/make/basic/t3/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/basic/t3/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t3/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/basic/t3/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/basic/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/legacy_test OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/execution/ellipsis/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/execution/empty/legacy_test OLD_FILES+=usr/tests/usr.bin/make/execution/empty/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/execution/empty/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/execution/empty/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/execution/empty/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/execution/empty/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/legacy_test OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/execution/joberr/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/execution/plus/legacy_test OLD_FILES+=usr/tests/usr.bin/make/execution/plus/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/execution/plus/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/execution/plus/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/execution/plus/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/execution/plus/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/execution/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/builtin/sh OLD_FILES+=usr/tests/usr.bin/make/shell/meta/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/meta/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/meta/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/meta/sh OLD_FILES+=usr/tests/usr.bin/make/shell/path/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/path/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/path/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path/sh OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/path_select/shell OLD_FILES+=usr/tests/usr.bin/make/shell/replace/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/replace/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/replace/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/replace/shell OLD_FILES+=usr/tests/usr.bin/make/shell/select/legacy_test OLD_FILES+=usr/tests/usr.bin/make/shell/select/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/shell/select/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/shell/select/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/shell/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/legacy_test OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/TEST1.a OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/basic/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/legacy_test OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/TEST1.a OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/TEST2.a OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild1/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/legacy_test OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/TEST1.a OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/TEST2.a OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/src_wild2/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/suffixes/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/legacy_test OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/directive-t0/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/legacy_test OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.status.3 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.status.4 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.status.5 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stderr.3 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stderr.4 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stderr.5 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stdout.3 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stdout.4 OLD_FILES+=usr/tests/usr.bin/make/syntax/enl/expected.stdout.5 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/legacy_test OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/funny-targets/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/legacy_test OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/syntax/semi/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/syntax/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/legacy_test OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/1/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/mk/sys.mk OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/mk/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t0/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/legacy_test OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/cleanup OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/1/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/mk/sys.mk OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/mk/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/legacy_test OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/cleanup OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/1/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/mk/sys.mk OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/mk/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/t2/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/sysmk/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/legacy_test OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_M/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/legacy_test OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.status.3 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stderr.3 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/variables/modifier_t/expected.stdout.3 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/legacy_test OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.status.2 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.stderr.2 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/variables/opt_V/expected.stdout.2 OLD_FILES+=usr/tests/usr.bin/make/variables/t0/legacy_test OLD_FILES+=usr/tests/usr.bin/make/variables/t0/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/variables/t0/Makefile.test OLD_FILES+=usr/tests/usr.bin/make/variables/t0/expected.status.1 OLD_FILES+=usr/tests/usr.bin/make/variables/t0/expected.stderr.1 OLD_FILES+=usr/tests/usr.bin/make/variables/t0/expected.stdout.1 OLD_FILES+=usr/tests/usr.bin/make/variables/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/Kyuafile OLD_FILES+=usr/tests/usr.bin/make/common.sh OLD_FILES+=usr/tests/usr.bin/make/test-new.mk OLD_DIRS+=usr/tests/usr.bin/make/variables/t0 OLD_DIRS+=usr/tests/usr.bin/make/variables/opt_V OLD_DIRS+=usr/tests/usr.bin/make/variables/modifier_t OLD_DIRS+=usr/tests/usr.bin/make/variables/modifier_M OLD_DIRS+=usr/tests/usr.bin/make/variables OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t2/mk OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t2/2/1 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t2/2 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t2 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t1/mk OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t1/2/1 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t1/2 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t1 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t0/mk OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t0/2/1 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t0/2 OLD_DIRS+=usr/tests/usr.bin/make/sysmk/t0 OLD_DIRS+=usr/tests/usr.bin/make/sysmk OLD_DIRS+=usr/tests/usr.bin/make/syntax/semi OLD_DIRS+=usr/tests/usr.bin/make/syntax/funny-targets OLD_DIRS+=usr/tests/usr.bin/make/syntax/enl OLD_DIRS+=usr/tests/usr.bin/make/syntax/directive-t0 OLD_DIRS+=usr/tests/usr.bin/make/syntax OLD_DIRS+=usr/tests/usr.bin/make/suffixes/src_wild2 OLD_DIRS+=usr/tests/usr.bin/make/suffixes/src_wild1 OLD_DIRS+=usr/tests/usr.bin/make/suffixes/basic OLD_DIRS+=usr/tests/usr.bin/make/suffixes OLD_DIRS+=usr/tests/usr.bin/make/shell/select OLD_DIRS+=usr/tests/usr.bin/make/shell/replace OLD_DIRS+=usr/tests/usr.bin/make/shell/path_select OLD_DIRS+=usr/tests/usr.bin/make/shell/path OLD_DIRS+=usr/tests/usr.bin/make/shell/meta OLD_DIRS+=usr/tests/usr.bin/make/shell/builtin OLD_DIRS+=usr/tests/usr.bin/make/shell OLD_DIRS+=usr/tests/usr.bin/make/execution/plus OLD_DIRS+=usr/tests/usr.bin/make/execution/joberr OLD_DIRS+=usr/tests/usr.bin/make/execution/empty OLD_DIRS+=usr/tests/usr.bin/make/execution/ellipsis OLD_DIRS+=usr/tests/usr.bin/make/execution OLD_DIRS+=usr/tests/usr.bin/make/basic/t3 OLD_DIRS+=usr/tests/usr.bin/make/basic/t2 OLD_DIRS+=usr/tests/usr.bin/make/basic/t1 OLD_DIRS+=usr/tests/usr.bin/make/basic/t0 OLD_DIRS+=usr/tests/usr.bin/make/basic OLD_DIRS+=usr/tests/usr.bin/make/archives/fmt_oldbsd OLD_DIRS+=usr/tests/usr.bin/make/archives/fmt_44bsd_mod OLD_DIRS+=usr/tests/usr.bin/make/archives/fmt_44bsd OLD_DIRS+=usr/tests/usr.bin/make/archives OLD_DIRS+=usr/tests/usr.bin/make OLD_FILES+=usr/tests/usr.bin/yacc/legacy_test OLD_FILES+=usr/tests/usr.bin/yacc/regress.00.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.01.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.02.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.03.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.04.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.05.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.06.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.07.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.08.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.09.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.10.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.11.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.12.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.13.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.14.out OLD_FILES+=usr/tests/usr.bin/yacc/regress.sh OLD_FILES+=usr/tests/usr.bin/yacc/undefined.y .endif .else # ATF libraries. +OLD_FILES+=etc/mtree/BSD.tests.dist OLD_FILES+=usr/bin/atf-sh OLD_DIRS+=usr/include/atf-c OLD_FILES+=usr/include/atf-c/build.h OLD_FILES+=usr/include/atf-c/check.h OLD_FILES+=usr/include/atf-c/config.h OLD_FILES+=usr/include/atf-c/defs.h OLD_FILES+=usr/include/atf-c/error.h OLD_FILES+=usr/include/atf-c/error_fwd.h OLD_FILES+=usr/include/atf-c/macros.h OLD_FILES+=usr/include/atf-c/tc.h OLD_FILES+=usr/include/atf-c/tp.h OLD_FILES+=usr/include/atf-c/utils.h OLD_FILES+=usr/include/atf-c.h OLD_DIRS+=usr/include/atf-c++ OLD_FILES+=usr/include/atf-c++/build.hpp OLD_FILES+=usr/include/atf-c++/check.hpp OLD_FILES+=usr/include/atf-c++/config.hpp OLD_FILES+=usr/include/atf-c++/macros.hpp OLD_FILES+=usr/include/atf-c++/tests.hpp OLD_FILES+=usr/include/atf-c++/utils.hpp OLD_FILES+=usr/include/atf-c++.hpp OLD_FILES+=usr/lib/libatf-c_p.a OLD_LIBS+=usr/lib/libatf-c.so.1 OLD_FILES+=usr/lib/libatf-c.so OLD_FILES+=usr/lib/libatf-c++.a OLD_FILES+=usr/lib/libatf-c++_p.a OLD_LIBS+=usr/lib/libatf-c++.so.1 OLD_FILES+=usr/lib/libatf-c++.so OLD_FILES+=usr/lib/libatf-c.a OLD_FILES+=usr/libexec/atf-check +OLD_FILES+=usr/libexec/atf-sh OLD_DIRS+=usr/share/atf OLD_FILES+=usr/share/atf/libatf-sh.subr OLD_DIRS+=usr/share/doc/atf OLD_FILES+=usr/share/doc/atf/AUTHORS OLD_FILES+=usr/share/doc/atf/COPYING OLD_FILES+=usr/share/doc/atf/NEWS OLD_FILES+=usr/share/doc/atf/README +OLD_FILES+=usr/share/doc/pjdfstest/README OLD_FILES+=usr/share/man/man1/atf-check.1.gz OLD_FILES+=usr/share/man/man1/atf-sh.1.gz OLD_FILES+=usr/share/man/man1/atf-test-program.1.gz OLD_FILES+=usr/share/man/man3/atf-c-api.3.gz OLD_FILES+=usr/share/man/man3/atf-c++-api.3.gz OLD_FILES+=usr/share/man/man3/atf-sh-api.3.gz +OLD_FILES+=usr/share/man/man3/atf-sh.3.gz OLD_FILES+=usr/share/man/man4/atf-test-case.4.gz +OLD_FILES+=usr/share/man/man7/atf.7.gz OLD_FILES+=usr/share/mk/atf.test.mk +OLD_FILES+=usr/share/mk/plain.test.mk +OLD_FILES+=usr/share/mk/suite.test.mk +OLD_FILES+=usr/share/mk/tap.test.mk # Test suite. . if exists(${DESTDIR}${TESTSBASE}) TESTS_DIRS!=find ${DESTDIR}${TESTSBASE} -type d | sed -e 's,^${DESTDIR}/,,'; echo OLD_DIRS+=${TESTS_DIRS} TESTS_FILES!=find ${DESTDIR}${TESTSBASE} \! -type d | sed -e 's,^${DESTDIR}/,,'; echo OLD_FILES+=${TESTS_FILES} . endif .endif # Test suite. -.if ${MK_TIMED} == no -OLD_FILES+=etc/rc.d/timed -OLD_FILES+=usr/sbin/timed -OLD_FILES+=usr/sbin/timedc -OLD_FILES+=usr/share/man/man8/timed.8.gz -OLD_FILES+=usr/share/man/man8/timedc.8.gz +.if ${MK_TEXTPROC} == no +OLD_FILES+=usr/bin/checknr +OLD_FILES+=usr/bin/colcrt +OLD_FILES+=usr/bin/ul +OLD_FILES+=usr/share/man/man1/checknr.1.gz +OLD_FILES+=usr/share/man/man1/colcrt.1.gz +OLD_FILES+=usr/share/man/man1/ul.1.gz .endif #.if ${MK_TOOLCHAIN} == no # to be filled in #.endif .if ${MK_UNBOUND} == no OLD_FILES+=etc/rc.d/local_unbound +OLD_FILES+=etc/unbound OLD_FILES+=usr/lib/private/libunbound.a OLD_FILES+=usr/lib/private/libunbound.so OLD_LIBS+=usr/lib/private/libunbound.so.5 OLD_FILES+=usr/lib/private/libunbound_p.a .if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/private/libunbound.a OLD_FILES+=usr/lib32/private/libunbound.so OLD_LIBS+=usr/lib32/private/libunbound.so.5 OLD_FILES+=usr/lib32/private/libunbound_p.a .endif OLD_FILES+=usr/sbin/local-unbound-setup OLD_FILES+=usr/sbin/unbound OLD_FILES+=usr/sbin/unbound-anchor OLD_FILES+=usr/sbin/unbound-checkconf OLD_FILES+=usr/sbin/unbound-control OLD_FILES+=usr/sbin/unbound-control-setup +OLD_FILES+=usr/share/man/man5/unbound.conf.5.gz +OLD_FILES+=usr/share/man/man8/unbound-anchor.8.gz +OLD_FILES+=usr/share/man/man8/unbound-checkconf.8.gz +OLD_FILES+=usr/share/man/man8/unbound-control.8.gz +OLD_FILES+=usr/share/man/man8/unbound.8.gz .endif .if ${MK_USB} == no +OLD_FILES+=etc/devd/uath.conf OLD_FILES+=etc/devd/uauth.conf +OLD_FILES+=etc/devd/ulpt.conf OLD_FILES+=etc/devd/usb.conf OLD_FILES+=usr/bin/usbhidaction OLD_FILES+=usr/bin/usbhidctl -OLD_FILES+=usr/include/dev/usb/ufm_ioctl.h -OLD_FILES+=usr/include/dev/usb/uftdiio.h -OLD_FILES+=usr/include/dev/usb/uled_ioctl.h -OLD_FILES+=usr/include/dev/usb/usb.h -OLD_FILES+=usr/include/dev/usb/usb_bus.h -OLD_FILES+=usr/include/dev/usb/usb_busdma.h -OLD_FILES+=usr/include/dev/usb/usb_cdc.h -OLD_FILES+=usr/include/dev/usb/usb_compat_linux.h -OLD_FILES+=usr/include/dev/usb/usb_controller.h -OLD_FILES+=usr/include/dev/usb/usb_core.h -OLD_FILES+=usr/include/dev/usb/usb_debug.h -OLD_FILES+=usr/include/dev/usb/usb_dev.h -OLD_FILES+=usr/include/dev/usb/usb_device.h -OLD_FILES+=usr/include/dev/usb/usb_dynamic.h -OLD_FILES+=usr/include/dev/usb/usb_endian.h -OLD_FILES+=usr/include/dev/usb/usb_freebsd.h -OLD_FILES+=usr/include/dev/usb/usb_freebsd_loader.h -OLD_FILES+=usr/include/dev/usb/usb_generic.h -OLD_FILES+=usr/include/dev/usb/usb_hub.h -OLD_FILES+=usr/include/dev/usb/usb_ioctl.h -OLD_FILES+=usr/include/dev/usb/usb_mbuf.h -OLD_FILES+=usr/include/dev/usb/usb_msctest.h -OLD_FILES+=usr/include/dev/usb/usb_pci.h -OLD_FILES+=usr/include/dev/usb/usb_pf.h -OLD_FILES+=usr/include/dev/usb/usb_process.h -OLD_FILES+=usr/include/dev/usb/usb_request.h -OLD_FILES+=usr/include/dev/usb/usb_transfer.h -OLD_FILES+=usr/include/dev/usb/usb_util.h -OLD_FILES+=usr/include/dev/usb/usbdi.h -OLD_FILES+=usr/include/dev/usb/usbdi_util.h -OLD_FILES+=usr/include/dev/usb/usbhid.h -OLD_DIRS+=usr/include/dev/usb OLD_FILES+=usr/include/libusb.h OLD_FILES+=usr/include/libusb20.h OLD_FILES+=usr/include/libusb20_desc.h OLD_FILES+=usr/include/usb.h OLD_FILES+=usr/include/usbhid.h OLD_FILES+=usr/lib/libusb.a +OLD_FILES+=usr/lib/libusb.so OLD_LIBS+=usr/lib/libusb.so.3 OLD_FILES+=usr/lib/libusb_p.a OLD_FILES+=usr/lib/libusbhid.a +OLD_FILES+=usr/lib/libusbhid.so OLD_LIBS+=usr/lib/libusbhid.so.4 OLD_FILES+=usr/lib/libusbhid_p.a -.if ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "powerpc64" OLD_FILES+=usr/lib32/libusb.a +OLD_FILES+=usr/lib32/libusb.so OLD_LIBS+=usr/lib32/libusb.so.3 +OLD_FILES+=usr/lib32/libusb_p.a OLD_FILES+=usr/lib32/libusbhid.a +OLD_FILES+=usr/lib32/libusbhid.so OLD_LIBS+=usr/lib32/libusbhid.so.4 +OLD_FILES+=usr/lib32/libusbhid_p.a +OLD_FILES+=usr/libdata/pkgconfig/libusb-0.1.pc +OLD_FILES+=usr/libdata/pkgconfig/libusb-1.0.pc +OLD_FILES+=usr/libdata/pkgconfig/libusb-2.0.pc +OLD_FILES+=usr/sbin/uathload +OLD_FILES+=usr/sbin/uhsoctl +OLD_FILES+=usr/sbin/usbconfig +OLD_FILES+=usr/sbin/usbdump +OLD_FILES+=usr/share/man/man1/uhsoctl.1.gz +OLD_FILES+=usr/share/man/man1/usbhidaction.1.gz +OLD_FILES+=usr/share/man/man1/usbhidctl.1.gz +OLD_FILES+=usr/share/man/man3/hid_dispose_report_desc.3.gz +OLD_FILES+=usr/share/man/man3/hid_end_parse.3.gz +OLD_FILES+=usr/share/man/man3/hid_get_data.3.gz +OLD_FILES+=usr/share/man/man3/hid_get_item.3.gz +OLD_FILES+=usr/share/man/man3/hid_get_report_desc.3.gz +OLD_FILES+=usr/share/man/man3/hid_init.3.gz +OLD_FILES+=usr/share/man/man3/hid_locate.3.gz +OLD_FILES+=usr/share/man/man3/hid_report_size.3.gz +OLD_FILES+=usr/share/man/man3/hid_set_data.3.gz +OLD_FILES+=usr/share/man/man3/hid_start_parse.3.gz +OLD_FILES+=usr/share/man/man3/hid_usage_in_page.3.gz +OLD_FILES+=usr/share/man/man3/hid_usage_page.3.gz +OLD_FILES+=usr/share/man/man3/libusb.3.gz +OLD_FILES+=usr/share/man/man3/libusb20.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_add_dev_quirk.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_alloc_default.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_dequeue_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_device_foreach.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_enqueue_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_free.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_get_dev_quirk.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_get_quirk_name.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_get_template.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_remove_dev_quirk.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_be_set_template.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_desc_foreach.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_alloc.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_alloc_config.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_check_connected.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_close.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_detach_kernel_driver.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_free.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_address.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_backend_name.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_bus_number.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_config_index.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_debug.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_desc.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_device_desc.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_fd.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_iface_desc.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_info.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_mode.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_parent_address.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_parent_port.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_port_path.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_power_mode.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_power_usage.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_get_speed.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_kernel_driver_active.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_open.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_process.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_req_string_simple_sync.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_req_string_sync.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_request_sync.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_reset.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_set_alt_index.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_set_config_index.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_set_debug.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_set_power_mode.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_dev_wait_process.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_error_name.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_me_decode.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_me_encode.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_me_get_1.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_me_get_2.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_strerror.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_bulk_intr_sync.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_callback_wrapper.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_clear_stall_sync.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_close.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_drain.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_actual_frames.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_actual_length.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_length.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_frames.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_packet_length.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_total_length.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_pointer.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_priv_sc0.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_priv_sc1.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_status.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_get_time_complete.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_open.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_pending.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_buffer.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_callback.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_flags.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_length.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_priv_sc0.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_priv_sc1.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_timeout.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_set_total_frames.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_bulk.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_control.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_intr.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_isoc.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_start.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_stop.3.gz +OLD_FILES+=usr/share/man/man3/libusb20_tr_submit.3.gz +OLD_FILES+=usr/share/man/man3/libusb_alloc_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_attach_kernel_driver.3.gz +OLD_FILES+=usr/share/man/man3/libusb_bulk_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_cancel_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_check_connected.3.gz +OLD_FILES+=usr/share/man/man3/libusb_claim_interface.3.gz +OLD_FILES+=usr/share/man/man3/libusb_clear_halt.3.gz +OLD_FILES+=usr/share/man/man3/libusb_close.3.gz +OLD_FILES+=usr/share/man/man3/libusb_control_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_detach_kernel_driver.3.gz +OLD_FILES+=usr/share/man/man3/libusb_detach_kernel_driver_np.3.gz +OLD_FILES+=usr/share/man/man3/libusb_error_name.3.gz +OLD_FILES+=usr/share/man/man3/libusb_event_handler_active.3.gz +OLD_FILES+=usr/share/man/man3/libusb_event_handling_ok.3.gz +OLD_FILES+=usr/share/man/man3/libusb_exit.3.gz +OLD_FILES+=usr/share/man/man3/libusb_free_bos_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_free_config_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_free_device_list.3.gz +OLD_FILES+=usr/share/man/man3/libusb_free_ss_endpoint_comp.3.gz +OLD_FILES+=usr/share/man/man3/libusb_free_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_active_config_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_bus_number.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_config_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_config_descriptor_by_value.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_configuration.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_device_address.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_device_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_device_list.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_device_speed.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_driver.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_driver_np.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_max_iso_packet_size.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_max_packet_size.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_next_timeout.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_pollfds.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_string_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_get_string_descriptor_ascii.3.gz +OLD_FILES+=usr/share/man/man3/libusb_handle_events.3.gz +OLD_FILES+=usr/share/man/man3/libusb_handle_events_completed.3.gz +OLD_FILES+=usr/share/man/man3/libusb_handle_events_locked.3.gz +OLD_FILES+=usr/share/man/man3/libusb_handle_events_timeout.3.gz +OLD_FILES+=usr/share/man/man3/libusb_handle_events_timeout_completed.3.gz +OLD_FILES+=usr/share/man/man3/libusb_init.3.gz +OLD_FILES+=usr/share/man/man3/libusb_interrupt_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_kernel_driver_active.3.gz +OLD_FILES+=usr/share/man/man3/libusb_lock_event_waiters.3.gz +OLD_FILES+=usr/share/man/man3/libusb_lock_events.3.gz +OLD_FILES+=usr/share/man/man3/libusb_open.3.gz +OLD_FILES+=usr/share/man/man3/libusb_open_device_with_vid_pid.3.gz +OLD_FILES+=usr/share/man/man3/libusb_parse_bos_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/libusb_parse_ss_endpoint_comp.3.gz +OLD_FILES+=usr/share/man/man3/libusb_ref_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb_release_interface.3.gz +OLD_FILES+=usr/share/man/man3/libusb_reset_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb_set_configuration.3.gz +OLD_FILES+=usr/share/man/man3/libusb_set_debug.3.gz +OLD_FILES+=usr/share/man/man3/libusb_set_interface_alt_setting.3.gz +OLD_FILES+=usr/share/man/man3/libusb_set_pollfd_notifiers.3.gz +OLD_FILES+=usr/share/man/man3/libusb_strerror.3.gz +OLD_FILES+=usr/share/man/man3/libusb_submit_transfer.3.gz +OLD_FILES+=usr/share/man/man3/libusb_try_lock_events.3.gz +OLD_FILES+=usr/share/man/man3/libusb_unlock_event_waiters.3.gz +OLD_FILES+=usr/share/man/man3/libusb_unlock_events.3.gz +OLD_FILES+=usr/share/man/man3/libusb_unref_device.3.gz +OLD_FILES+=usr/share/man/man3/libusb_wait_for_event.3.gz +OLD_FILES+=usr/share/man/man3/libusbhid.3.gz +OLD_FILES+=usr/share/man/man3/usb.3.gz +OLD_FILES+=usr/share/man/man3/usb_bulk_read.3.gz +OLD_FILES+=usr/share/man/man3/usb_bulk_write.3.gz +OLD_FILES+=usr/share/man/man3/usb_check_connected.3.gz +OLD_FILES+=usr/share/man/man3/usb_claim_interface.3.gz +OLD_FILES+=usr/share/man/man3/usb_clear_halt.3.gz +OLD_FILES+=usr/share/man/man3/usb_close.3.gz +OLD_FILES+=usr/share/man/man3/usb_control_msg.3.gz +OLD_FILES+=usr/share/man/man3/usb_destroy_configuration.3.gz +OLD_FILES+=usr/share/man/man3/usb_device.3.gz +OLD_FILES+=usr/share/man/man3/usb_fetch_and_parse_descriptors.3.gz +OLD_FILES+=usr/share/man/man3/usb_find_busses.3.gz +OLD_FILES+=usr/share/man/man3/usb_find_devices.3.gz +OLD_FILES+=usr/share/man/man3/usb_get_busses.3.gz +OLD_FILES+=usr/share/man/man3/usb_get_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/usb_get_descriptor_by_endpoint.3.gz +OLD_FILES+=usr/share/man/man3/usb_get_string.3.gz +OLD_FILES+=usr/share/man/man3/usb_get_string_simple.3.gz +OLD_FILES+=usr/share/man/man3/usb_init.3.gz +OLD_FILES+=usr/share/man/man3/usb_interrupt_read.3.gz +OLD_FILES+=usr/share/man/man3/usb_interrupt_write.3.gz +OLD_FILES+=usr/share/man/man3/usb_open.3.gz +OLD_FILES+=usr/share/man/man3/usb_parse_configuration.3.gz +OLD_FILES+=usr/share/man/man3/usb_parse_descriptor.3.gz +OLD_FILES+=usr/share/man/man3/usb_release_interface.3.gz +OLD_FILES+=usr/share/man/man3/usb_reset.3.gz +OLD_FILES+=usr/share/man/man3/usb_resetep.3.gz +OLD_FILES+=usr/share/man/man3/usb_set_altinterface.3.gz +OLD_FILES+=usr/share/man/man3/usb_set_configuration.3.gz +OLD_FILES+=usr/share/man/man3/usb_set_debug.3.gz +OLD_FILES+=usr/share/man/man3/usb_strerror.3.gz +OLD_FILES+=usr/share/man/man3/usbhid.3.gz +OLD_FILES+=usr/share/man/man8/uathload.8.gz +OLD_FILES+=usr/share/man/man8/usbconfig.8.gz +OLD_FILES+=usr/share/man/man8/usbdump.8.gz .endif OLD_FILES+=usr/libdata/pkgconfig/libusb-0.1.pc OLD_FILES+=usr/libdata/pkgconfig/libusb-1.0.pc OLD_FILES+=usr/libdata/pkgconfig/libusb-2.0.pc OLD_FILES+=usr/sbin/usbconfig OLD_FILES+=usr/sbin/usbdump OLD_FILES+=usr/share/examples/libusb20/Makefile OLD_FILES+=usr/share/examples/libusb20/README OLD_FILES+=usr/share/examples/libusb20/bulk.c OLD_FILES+=usr/share/examples/libusb20/control.c OLD_FILES+=usr/share/examples/libusb20/util.c OLD_FILES+=usr/share/examples/libusb20/util.h OLD_DIRS+=usr/share/examples/libusb20 OLD_FILES+=usr/share/man/man1/usbhidaction.1.gz OLD_FILES+=usr/share/man/man1/usbhidctl.1.gz OLD_FILES+=usr/share/man/man3/hid_dispose_report_desc.3.gz OLD_FILES+=usr/share/man/man3/hid_end_parse.3.gz OLD_FILES+=usr/share/man/man3/hid_get_data.3.gz OLD_FILES+=usr/share/man/man3/hid_get_item.3.gz OLD_FILES+=usr/share/man/man3/hid_get_report_desc.3.gz OLD_FILES+=usr/share/man/man3/hid_init.3.gz OLD_FILES+=usr/share/man/man3/hid_locate.3.gz OLD_FILES+=usr/share/man/man3/hid_report_size.3.gz OLD_FILES+=usr/share/man/man3/hid_set_data.3.gz OLD_FILES+=usr/share/man/man3/hid_start_parse.3.gz OLD_FILES+=usr/share/man/man3/hid_usage_in_page.3.gz OLD_FILES+=usr/share/man/man3/hid_usage_page.3.gz OLD_FILES+=usr/share/man/man3/libusb.3.gz OLD_FILES+=usr/share/man/man3/libusb20.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_add_dev_quirk.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_alloc_default.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_dequeue_device.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_device_foreach.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_enqueue_device.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_free.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_get_dev_quirk.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_get_quirk_name.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_get_template.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_remove_dev_quirk.3.gz OLD_FILES+=usr/share/man/man3/libusb20_be_set_template.3.gz OLD_FILES+=usr/share/man/man3/libusb20_desc_foreach.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_alloc.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_alloc_config.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_check_connected.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_close.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_detach_kernel_driver.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_free.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_address.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_backend_name.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_bus_number.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_config_index.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_debug.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_desc.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_device_desc.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_fd.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_iface_desc.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_info.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_mode.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_parent_address.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_parent_port.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_port_path.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_power_mode.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_power_usage.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_get_speed.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_kernel_driver_active.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_open.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_process.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_req_string_simple_sync.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_req_string_sync.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_request_sync.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_reset.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_set_alt_index.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_set_config_index.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_set_debug.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_set_power_mode.3.gz OLD_FILES+=usr/share/man/man3/libusb20_dev_wait_process.3.gz OLD_FILES+=usr/share/man/man3/libusb20_error_name.3.gz OLD_FILES+=usr/share/man/man3/libusb20_me_decode.3.gz OLD_FILES+=usr/share/man/man3/libusb20_me_encode.3.gz OLD_FILES+=usr/share/man/man3/libusb20_me_get_1.3.gz OLD_FILES+=usr/share/man/man3/libusb20_me_get_2.3.gz OLD_FILES+=usr/share/man/man3/libusb20_strerror.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_bulk_intr_sync.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_callback_wrapper.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_clear_stall_sync.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_close.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_drain.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_actual_frames.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_actual_length.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_length.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_frames.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_packet_length.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_max_total_length.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_pointer.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_priv_sc0.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_priv_sc1.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_status.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_get_time_complete.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_open.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_pending.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_buffer.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_callback.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_flags.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_length.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_priv_sc0.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_priv_sc1.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_timeout.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_set_total_frames.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_bulk.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_control.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_intr.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_setup_isoc.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_start.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_stop.3.gz OLD_FILES+=usr/share/man/man3/libusb20_tr_submit.3.gz OLD_FILES+=usr/share/man/man3/libusb_alloc_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_attach_kernel_driver.3.gz OLD_FILES+=usr/share/man/man3/libusb_bulk_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_cancel_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_check_connected.3.gz OLD_FILES+=usr/share/man/man3/libusb_claim_interface.3.gz OLD_FILES+=usr/share/man/man3/libusb_clear_halt.3.gz OLD_FILES+=usr/share/man/man3/libusb_close.3.gz OLD_FILES+=usr/share/man/man3/libusb_control_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_detach_kernel_driver.3.gz OLD_FILES+=usr/share/man/man3/libusb_detach_kernel_driver_np.3.gz OLD_FILES+=usr/share/man/man3/libusb_error_name.3.gz OLD_FILES+=usr/share/man/man3/libusb_event_handler_active.3.gz OLD_FILES+=usr/share/man/man3/libusb_event_handling_ok.3.gz OLD_FILES+=usr/share/man/man3/libusb_exit.3.gz OLD_FILES+=usr/share/man/man3/libusb_free_bos_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_free_config_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_free_device_list.3.gz OLD_FILES+=usr/share/man/man3/libusb_free_ss_endpoint_comp.3.gz OLD_FILES+=usr/share/man/man3/libusb_free_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_active_config_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_bus_number.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_config_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_config_descriptor_by_value.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_configuration.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_device.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_device_address.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_device_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_device_list.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_device_speed.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_driver.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_driver_np.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_max_iso_packet_size.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_max_packet_size.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_next_timeout.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_pollfds.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_string_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_get_string_descriptor_ascii.3.gz OLD_FILES+=usr/share/man/man3/libusb_handle_events.3.gz OLD_FILES+=usr/share/man/man3/libusb_handle_events_completed.3.gz OLD_FILES+=usr/share/man/man3/libusb_handle_events_locked.3.gz OLD_FILES+=usr/share/man/man3/libusb_handle_events_timeout.3.gz OLD_FILES+=usr/share/man/man3/libusb_handle_events_timeout_completed.3.gz OLD_FILES+=usr/share/man/man3/libusb_init.3.gz OLD_FILES+=usr/share/man/man3/libusb_interrupt_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_kernel_driver_active.3.gz OLD_FILES+=usr/share/man/man3/libusb_lock_event_waiters.3.gz OLD_FILES+=usr/share/man/man3/libusb_lock_events.3.gz OLD_FILES+=usr/share/man/man3/libusb_open.3.gz OLD_FILES+=usr/share/man/man3/libusb_open_device_with_vid_pid.3.gz OLD_FILES+=usr/share/man/man3/libusb_parse_bos_descriptor.3.gz OLD_FILES+=usr/share/man/man3/libusb_parse_ss_endpoint_comp.3.gz OLD_FILES+=usr/share/man/man3/libusb_ref_device.3.gz OLD_FILES+=usr/share/man/man3/libusb_release_interface.3.gz OLD_FILES+=usr/share/man/man3/libusb_reset_device.3.gz OLD_FILES+=usr/share/man/man3/libusb_set_configuration.3.gz OLD_FILES+=usr/share/man/man3/libusb_set_debug.3.gz OLD_FILES+=usr/share/man/man3/libusb_set_interface_alt_setting.3.gz OLD_FILES+=usr/share/man/man3/libusb_set_pollfd_notifiers.3.gz OLD_FILES+=usr/share/man/man3/libusb_strerror.3.gz OLD_FILES+=usr/share/man/man3/libusb_submit_transfer.3.gz OLD_FILES+=usr/share/man/man3/libusb_try_lock_events.3.gz OLD_FILES+=usr/share/man/man3/libusb_unlock_event_waiters.3.gz OLD_FILES+=usr/share/man/man3/libusb_unlock_events.3.gz OLD_FILES+=usr/share/man/man3/libusb_unref_device.3.gz OLD_FILES+=usr/share/man/man3/libusb_wait_for_event.3.gz OLD_FILES+=usr/share/man/man3/libusbhid.3.gz OLD_FILES+=usr/share/man/man3/usb.3.gz OLD_FILES+=usr/share/man/man3/usb_bulk_read.3.gz OLD_FILES+=usr/share/man/man3/usb_bulk_write.3.gz OLD_FILES+=usr/share/man/man3/usb_check_connected.3.gz OLD_FILES+=usr/share/man/man3/usb_claim_interface.3.gz OLD_FILES+=usr/share/man/man3/usb_clear_halt.3.gz OLD_FILES+=usr/share/man/man3/usb_close.3.gz OLD_FILES+=usr/share/man/man3/usb_control_msg.3.gz OLD_FILES+=usr/share/man/man3/usb_destroy_configuration.3.gz OLD_FILES+=usr/share/man/man3/usb_device.3.gz OLD_FILES+=usr/share/man/man3/usb_fetch_and_parse_descriptors.3.gz OLD_FILES+=usr/share/man/man3/usb_find_busses.3.gz OLD_FILES+=usr/share/man/man3/usb_find_devices.3.gz OLD_FILES+=usr/share/man/man3/usb_get_busses.3.gz OLD_FILES+=usr/share/man/man3/usb_get_descriptor.3.gz OLD_FILES+=usr/share/man/man3/usb_get_descriptor_by_endpoint.3.gz OLD_FILES+=usr/share/man/man3/usb_get_string.3.gz OLD_FILES+=usr/share/man/man3/usb_get_string_simple.3.gz OLD_FILES+=usr/share/man/man3/usb_init.3.gz OLD_FILES+=usr/share/man/man3/usb_interrupt_read.3.gz OLD_FILES+=usr/share/man/man3/usb_interrupt_write.3.gz OLD_FILES+=usr/share/man/man3/usb_open.3.gz OLD_FILES+=usr/share/man/man3/usb_parse_configuration.3.gz OLD_FILES+=usr/share/man/man3/usb_parse_descriptor.3.gz OLD_FILES+=usr/share/man/man3/usb_release_interface.3.gz OLD_FILES+=usr/share/man/man3/usb_reset.3.gz OLD_FILES+=usr/share/man/man3/usb_resetep.3.gz OLD_FILES+=usr/share/man/man3/usb_set_altinterface.3.gz OLD_FILES+=usr/share/man/man3/usb_set_configuration.3.gz OLD_FILES+=usr/share/man/man3/usb_set_debug.3.gz OLD_FILES+=usr/share/man/man3/usb_strerror.3.gz OLD_FILES+=usr/share/man/man3/usbhid.3.gz OLD_FILES+=usr/share/man/man4/u3g.4.gz OLD_FILES+=usr/share/man/man4/u3gstub.4.gz OLD_FILES+=usr/share/man/man4/uark.4.gz OLD_FILES+=usr/share/man/man4/uart.4.gz OLD_FILES+=usr/share/man/man4/uath.4.gz OLD_FILES+=usr/share/man/man4/ubsa.4.gz OLD_FILES+=usr/share/man/man4/ubsec.4.gz OLD_FILES+=usr/share/man/man4/ubser.4.gz OLD_FILES+=usr/share/man/man4/ubtbcmfw.4.gz OLD_FILES+=usr/share/man/man4/uchcom.4.gz OLD_FILES+=usr/share/man/man4/ucom.4.gz OLD_FILES+=usr/share/man/man4/ucycom.4.gz OLD_FILES+=usr/share/man/man4/udav.4.gz OLD_FILES+=usr/share/man/man4/udbp.4.gz OLD_FILES+=usr/share/man/man4/udp.4.gz OLD_FILES+=usr/share/man/man4/udplite.4.gz OLD_FILES+=usr/share/man/man4/uep.4.gz OLD_FILES+=usr/share/man/man4/ufm.4.gz OLD_FILES+=usr/share/man/man4/ufoma.4.gz OLD_FILES+=usr/share/man/man4/uftdi.4.gz OLD_FILES+=usr/share/man/man4/ugen.4.gz OLD_FILES+=usr/share/man/man4/uhci.4.gz OLD_FILES+=usr/share/man/man4/uhid.4.gz OLD_FILES+=usr/share/man/man4/uhso.4.gz OLD_FILES+=usr/share/man/man4/uipaq.4.gz OLD_FILES+=usr/share/man/man4/ukbd.4.gz OLD_FILES+=usr/share/man/man4/uled.4.gz OLD_FILES+=usr/share/man/man4/ulpt.4.gz OLD_FILES+=usr/share/man/man4/umass.4.gz OLD_FILES+=usr/share/man/man4/umcs.4.gz OLD_FILES+=usr/share/man/man4/umct.4.gz OLD_FILES+=usr/share/man/man4/umodem.4.gz OLD_FILES+=usr/share/man/man4/umoscom.4.gz OLD_FILES+=usr/share/man/man4/ums.4.gz OLD_FILES+=usr/share/man/man4/unix.4.gz OLD_FILES+=usr/share/man/man4/upgt.4.gz OLD_FILES+=usr/share/man/man4/uplcom.4.gz OLD_FILES+=usr/share/man/man4/ural.4.gz OLD_FILES+=usr/share/man/man4/urio.4.gz OLD_FILES+=usr/share/man/man4/urndis.4.gz OLD_FILES+=usr/share/man/man4/urtw.4.gz OLD_FILES+=usr/share/man/man4/urtwn.4.gz OLD_FILES+=usr/share/man/man4/urtwnfw.4.gz OLD_FILES+=usr/share/man/man4/usb.4.gz OLD_FILES+=usr/share/man/man4/usb_quirk.4.gz OLD_FILES+=usr/share/man/man4/usb_template.4.gz OLD_FILES+=usr/share/man/man4/usfs.4.gz OLD_FILES+=usr/share/man/man4/uslcom.4.gz OLD_FILES+=usr/share/man/man4/utopia.4.gz OLD_FILES+=usr/share/man/man4/uvisor.4.gz OLD_FILES+=usr/share/man/man4/uvscom.4.gz OLD_FILES+=usr/share/man/man8/usbconfig.8.gz OLD_FILES+=usr/share/man/man8/usbdump.8.gz OLD_FILES+=usr/share/man/man9/usb_fifo_alloc_buffer.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_attach.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_detach.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_free_buffer.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_get_data.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_get_data_buffer.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_get_data_error.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_get_data_linear.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_put_bytes_max.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_put_data.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_put_data_buffer.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_put_data_error.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_put_data_linear.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_reset.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_softc.9.gz OLD_FILES+=usr/share/man/man9/usb_fifo_wakeup.9.gz OLD_FILES+=usr/share/man/man9/usbd_do_request.9.gz OLD_FILES+=usr/share/man/man9/usbd_do_request_flags.9.gz OLD_FILES+=usr/share/man/man9/usbd_errstr.9.gz OLD_FILES+=usr/share/man/man9/usbd_lookup_id_by_info.9.gz OLD_FILES+=usr/share/man/man9/usbd_lookup_id_by_uaa.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_clear_stall.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_drain.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_pending.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_poll.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_setup.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_start.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_stop.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_submit.9.gz OLD_FILES+=usr/share/man/man9/usbd_transfer_unsetup.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_clr_flag.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_frame_data.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_frame_len.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_get_frame.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_get_priv.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_is_stalled.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_max_framelen.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_max_frames.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_max_len.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_flag.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_frame_data.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_frame_len.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_frame_offset.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_frames.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_interval.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_priv.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_stall.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_set_timeout.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_softc.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_state.9.gz OLD_FILES+=usr/share/man/man9/usbd_xfer_status.9.gz OLD_FILES+=usr/share/man/man9/usbdi.9.gz OLD_FILES+=usr/share/misc/usb_hid_usages OLD_FILES+=usr/share/misc/usbdevs .endif .if ${MK_UTMPX} == no OLD_FILES+=etc/periodic/monthly/200.accounting OLD_FILES+=usr/bin/last OLD_FILES+=usr/bin/users OLD_FILES+=usr/bin/who OLD_FILES+=usr/sbin/ac OLD_FILES+=usr/sbin/lastlogin OLD_FILES+=usr/sbin/utx OLD_FILES+=usr/share/man/man1/last.1.gz OLD_FILES+=usr/share/man/man1/users.1.gz OLD_FILES+=usr/share/man/man1/who.1.gz OLD_FILES+=usr/share/man/man8/ac.8.gz OLD_FILES+=usr/share/man/man8/lastlogin.8.gz OLD_FILES+=usr/share/man/man8/utx.8.gz .endif .if ${MK_VI} == no OLD_FILES+=etc/rc.d/virecover OLD_FILES+=usr/bin/ex OLD_FILES+=usr/bin/nex OLD_FILES+=usr/bin/nvi OLD_FILES+=usr/bin/nview OLD_FILES+=usr/bin/vi OLD_FILES+=usr/bin/view OLD_FILES+=usr/share/man/man1/ex.1.gz OLD_FILES+=usr/share/man/man1/nex.1.gz OLD_FILES+=usr/share/man/man1/nvi.1.gz OLD_FILES+=usr/share/man/man1/nview.1.gz OLD_FILES+=usr/share/man/man1/vi.1.gz OLD_FILES+=usr/share/man/man1/view.1.gz OLD_FILES+=usr/share/vi/catalog/C OLD_FILES+=usr/share/vi/catalog/POSIX OLD_FILES+=usr/share/vi/catalog/de_AT.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/de_AT.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/de_AT.UTF-8 OLD_FILES+=usr/share/vi/catalog/de_CH.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/de_CH.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/de_CH.UTF-8 OLD_FILES+=usr/share/vi/catalog/de_DE.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/de_DE.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/de_DE.UTF-8 OLD_FILES+=usr/share/vi/catalog/dutch OLD_FILES+=usr/share/vi/catalog/dutch.UTF-8 OLD_FILES+=usr/share/vi/catalog/en_AU.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/en_AU.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/en_AU.US-ASCII OLD_FILES+=usr/share/vi/catalog/en_AU.UTF-8 OLD_FILES+=usr/share/vi/catalog/en_CA.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/en_CA.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/en_CA.US-ASCII OLD_FILES+=usr/share/vi/catalog/en_CA.UTF-8 OLD_FILES+=usr/share/vi/catalog/en_GB.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/en_GB.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/en_GB.US-ASCII OLD_FILES+=usr/share/vi/catalog/en_GB.UTF-8 OLD_FILES+=usr/share/vi/catalog/en_NZ.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/en_NZ.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/en_NZ.US-ASCII OLD_FILES+=usr/share/vi/catalog/en_NZ.UTF-8 OLD_FILES+=usr/share/vi/catalog/en_US.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/en_US.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/en_US.US-ASCII OLD_FILES+=usr/share/vi/catalog/en_US.UTF-8 OLD_FILES+=usr/share/vi/catalog/english OLD_FILES+=usr/share/vi/catalog/es_ES.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/es_ES.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/es_ES.UTF-8 OLD_FILES+=usr/share/vi/catalog/fr_BE.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/fr_BE.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/fr_BE.UTF-8 OLD_FILES+=usr/share/vi/catalog/fr_CA.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/fr_CA.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/fr_CA.UTF-8 OLD_FILES+=usr/share/vi/catalog/fr_CH.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/fr_CH.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/fr_CH.UTF-8 OLD_FILES+=usr/share/vi/catalog/fr_FR.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/fr_FR.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/fr_FR.UTF-8 OLD_FILES+=usr/share/vi/catalog/french OLD_FILES+=usr/share/vi/catalog/french.UTF-8 OLD_FILES+=usr/share/vi/catalog/german OLD_FILES+=usr/share/vi/catalog/german.UTF-8 OLD_FILES+=usr/share/vi/catalog/nl_NL.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/nl_NL.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/nl_NL.UTF-8 OLD_FILES+=usr/share/vi/catalog/pl_PL.ISO8859-2 OLD_FILES+=usr/share/vi/catalog/pl_PL.UTF-8 OLD_FILES+=usr/share/vi/catalog/polish OLD_FILES+=usr/share/vi/catalog/polish.UTF-8 OLD_FILES+=usr/share/vi/catalog/ru_RU.KOI8-R OLD_FILES+=usr/share/vi/catalog/ru_RU.UTF-8 OLD_FILES+=usr/share/vi/catalog/spanish OLD_FILES+=usr/share/vi/catalog/spanish.UTF-8 OLD_FILES+=usr/share/vi/catalog/sv_SE.ISO8859-1 OLD_FILES+=usr/share/vi/catalog/sv_SE.ISO8859-15 OLD_FILES+=usr/share/vi/catalog/sv_SE.UTF-8 OLD_FILES+=usr/share/vi/catalog/swedish OLD_FILES+=usr/share/vi/catalog/swedish.UTF-8 OLD_FILES+=usr/share/vi/catalog/uk_UA.KOI8-U OLD_FILES+=usr/share/vi/catalog/uk_UA.UTF-8 OLD_FILES+=usr/share/vi/catalog/zh_CN.GB18030 OLD_FILES+=usr/share/vi/catalog/zh_CN.GB2312 OLD_FILES+=usr/share/vi/catalog/zh_CN.GBK OLD_FILES+=usr/share/vi/catalog/zh_CN.UTF-8 OLD_FILES+=usr/share/vi/catalog/zh_CN.eucCN OLD_DIRS+=usr/share/vi/catalog OLD_DIRS+=usr/share/vi .endif .if ${MK_WIRELESS} == no OLD_FILES+=etc/regdomain.xml OLD_FILES+=etc/rc.d/hostapd OLD_FILES+=etc/rc.d/wpa_supplicant OLD_FILES+=usr/sbin/ancontrol OLD_FILES+=usr/sbin/hostapd OLD_FILES+=usr/sbin/hostapd_cli OLD_FILES+=usr/sbin/ndis_events OLD_FILES+=usr/sbin/wlandebug .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/sbin/wlconfig .endif OLD_FILES+=usr/sbin/wpa_cli OLD_FILES+=usr/sbin/wpa_passphrase OLD_FILES+=usr/sbin/wpa_supplicant OLD_FILES+=usr/share/examples/etc/regdomain.xml OLD_FILES+=usr/share/examples/etc/wpa_supplicant.conf OLD_FILES+=usr/share/examples/hostapd/hostapd.conf OLD_FILES+=usr/share/examples/hostapd/hostapd.eap_user OLD_FILES+=usr/share/examples/hostapd/hostapd.wpa_psk OLD_DIRS+=usr/share/examples/hostapd OLD_FILES+=usr/share/man/man5/hostapd.conf.5.gz OLD_FILES+=usr/share/man/man5/wpa_supplicant.conf.5.gz OLD_FILES+=usr/share/man/man8/ancontrol.8.gz OLD_FILES+=usr/share/man/man8/hostapd.8.gz OLD_FILES+=usr/share/man/man8/hostapd_cli.8.gz .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/share/man/man8/i386/wlconfig.8.gz .endif OLD_FILES+=usr/share/man/man8/ndis_events.8.gz OLD_FILES+=usr/share/man/man8/wlandebug.8.gz OLD_FILES+=usr/share/man/man8/wpa_cli.8.gz OLD_FILES+=usr/share/man/man8/wpa_passphrase.8.gz OLD_FILES+=usr/share/man/man8/wpa_supplicant.8.gz .endif .if ${MK_ZONEINFO} == no OLD_FILES+=usr/share/zoneinfo/Africa/Abidjan OLD_FILES+=usr/share/zoneinfo/Africa/Accra OLD_FILES+=usr/share/zoneinfo/Africa/Addis_Ababa OLD_FILES+=usr/share/zoneinfo/Africa/Algiers OLD_FILES+=usr/share/zoneinfo/Africa/Asmara OLD_FILES+=usr/share/zoneinfo/Africa/Bamako OLD_FILES+=usr/share/zoneinfo/Africa/Bangui OLD_FILES+=usr/share/zoneinfo/Africa/Banjul OLD_FILES+=usr/share/zoneinfo/Africa/Bissau OLD_FILES+=usr/share/zoneinfo/Africa/Blantyre OLD_FILES+=usr/share/zoneinfo/Africa/Brazzaville OLD_FILES+=usr/share/zoneinfo/Africa/Bujumbura OLD_FILES+=usr/share/zoneinfo/Africa/Cairo OLD_FILES+=usr/share/zoneinfo/Africa/Casablanca OLD_FILES+=usr/share/zoneinfo/Africa/Ceuta OLD_FILES+=usr/share/zoneinfo/Africa/Conakry OLD_FILES+=usr/share/zoneinfo/Africa/Dakar OLD_FILES+=usr/share/zoneinfo/Africa/Dar_es_Salaam OLD_FILES+=usr/share/zoneinfo/Africa/Djibouti OLD_FILES+=usr/share/zoneinfo/Africa/Douala OLD_FILES+=usr/share/zoneinfo/Africa/El_Aaiun OLD_FILES+=usr/share/zoneinfo/Africa/Freetown OLD_FILES+=usr/share/zoneinfo/Africa/Gaborone OLD_FILES+=usr/share/zoneinfo/Africa/Harare OLD_FILES+=usr/share/zoneinfo/Africa/Johannesburg OLD_FILES+=usr/share/zoneinfo/Africa/Juba OLD_FILES+=usr/share/zoneinfo/Africa/Kampala OLD_FILES+=usr/share/zoneinfo/Africa/Khartoum OLD_FILES+=usr/share/zoneinfo/Africa/Kigali OLD_FILES+=usr/share/zoneinfo/Africa/Kinshasa OLD_FILES+=usr/share/zoneinfo/Africa/Lagos OLD_FILES+=usr/share/zoneinfo/Africa/Libreville OLD_FILES+=usr/share/zoneinfo/Africa/Lome OLD_FILES+=usr/share/zoneinfo/Africa/Luanda OLD_FILES+=usr/share/zoneinfo/Africa/Lubumbashi OLD_FILES+=usr/share/zoneinfo/Africa/Lusaka OLD_FILES+=usr/share/zoneinfo/Africa/Malabo OLD_FILES+=usr/share/zoneinfo/Africa/Maputo OLD_FILES+=usr/share/zoneinfo/Africa/Maseru OLD_FILES+=usr/share/zoneinfo/Africa/Mbabane OLD_FILES+=usr/share/zoneinfo/Africa/Mogadishu OLD_FILES+=usr/share/zoneinfo/Africa/Monrovia OLD_FILES+=usr/share/zoneinfo/Africa/Nairobi OLD_FILES+=usr/share/zoneinfo/Africa/Ndjamena OLD_FILES+=usr/share/zoneinfo/Africa/Niamey OLD_FILES+=usr/share/zoneinfo/Africa/Nouakchott OLD_FILES+=usr/share/zoneinfo/Africa/Ouagadougou OLD_FILES+=usr/share/zoneinfo/Africa/Porto-Novo OLD_FILES+=usr/share/zoneinfo/Africa/Sao_Tome OLD_FILES+=usr/share/zoneinfo/Africa/Tripoli OLD_FILES+=usr/share/zoneinfo/Africa/Tunis OLD_FILES+=usr/share/zoneinfo/Africa/Windhoek OLD_FILES+=usr/share/zoneinfo/America/Adak OLD_FILES+=usr/share/zoneinfo/America/Anchorage OLD_FILES+=usr/share/zoneinfo/America/Anguilla OLD_FILES+=usr/share/zoneinfo/America/Antigua OLD_FILES+=usr/share/zoneinfo/America/Araguaina OLD_FILES+=usr/share/zoneinfo/America/Argentina/Buenos_Aires OLD_FILES+=usr/share/zoneinfo/America/Argentina/Catamarca OLD_FILES+=usr/share/zoneinfo/America/Argentina/Cordoba OLD_FILES+=usr/share/zoneinfo/America/Argentina/Jujuy OLD_FILES+=usr/share/zoneinfo/America/Argentina/La_Rioja OLD_FILES+=usr/share/zoneinfo/America/Argentina/Mendoza OLD_FILES+=usr/share/zoneinfo/America/Argentina/Rio_Gallegos OLD_FILES+=usr/share/zoneinfo/America/Argentina/Salta OLD_FILES+=usr/share/zoneinfo/America/Argentina/San_Juan OLD_FILES+=usr/share/zoneinfo/America/Argentina/San_Luis OLD_FILES+=usr/share/zoneinfo/America/Argentina/Tucuman OLD_FILES+=usr/share/zoneinfo/America/Argentina/Ushuaia OLD_FILES+=usr/share/zoneinfo/America/Aruba OLD_FILES+=usr/share/zoneinfo/America/Asuncion OLD_FILES+=usr/share/zoneinfo/America/Atikokan OLD_FILES+=usr/share/zoneinfo/America/Bahia OLD_FILES+=usr/share/zoneinfo/America/Bahia_Banderas OLD_FILES+=usr/share/zoneinfo/America/Barbados OLD_FILES+=usr/share/zoneinfo/America/Belem OLD_FILES+=usr/share/zoneinfo/America/Belize OLD_FILES+=usr/share/zoneinfo/America/Blanc-Sablon OLD_FILES+=usr/share/zoneinfo/America/Boa_Vista OLD_FILES+=usr/share/zoneinfo/America/Bogota OLD_FILES+=usr/share/zoneinfo/America/Boise OLD_FILES+=usr/share/zoneinfo/America/Cambridge_Bay OLD_FILES+=usr/share/zoneinfo/America/Campo_Grande OLD_FILES+=usr/share/zoneinfo/America/Cancun OLD_FILES+=usr/share/zoneinfo/America/Caracas OLD_FILES+=usr/share/zoneinfo/America/Cayenne OLD_FILES+=usr/share/zoneinfo/America/Cayman OLD_FILES+=usr/share/zoneinfo/America/Chicago OLD_FILES+=usr/share/zoneinfo/America/Chihuahua OLD_FILES+=usr/share/zoneinfo/America/Costa_Rica OLD_FILES+=usr/share/zoneinfo/America/Creston OLD_FILES+=usr/share/zoneinfo/America/Cuiaba OLD_FILES+=usr/share/zoneinfo/America/Curacao OLD_FILES+=usr/share/zoneinfo/America/Danmarkshavn OLD_FILES+=usr/share/zoneinfo/America/Dawson OLD_FILES+=usr/share/zoneinfo/America/Dawson_Creek OLD_FILES+=usr/share/zoneinfo/America/Denver OLD_FILES+=usr/share/zoneinfo/America/Detroit OLD_FILES+=usr/share/zoneinfo/America/Dominica OLD_FILES+=usr/share/zoneinfo/America/Edmonton OLD_FILES+=usr/share/zoneinfo/America/Eirunepe OLD_FILES+=usr/share/zoneinfo/America/El_Salvador OLD_FILES+=usr/share/zoneinfo/America/Fortaleza OLD_FILES+=usr/share/zoneinfo/America/Glace_Bay OLD_FILES+=usr/share/zoneinfo/America/Godthab OLD_FILES+=usr/share/zoneinfo/America/Goose_Bay OLD_FILES+=usr/share/zoneinfo/America/Grand_Turk OLD_FILES+=usr/share/zoneinfo/America/Grenada OLD_FILES+=usr/share/zoneinfo/America/Guadeloupe OLD_FILES+=usr/share/zoneinfo/America/Guatemala OLD_FILES+=usr/share/zoneinfo/America/Guayaquil OLD_FILES+=usr/share/zoneinfo/America/Guyana OLD_FILES+=usr/share/zoneinfo/America/Halifax OLD_FILES+=usr/share/zoneinfo/America/Havana OLD_FILES+=usr/share/zoneinfo/America/Hermosillo OLD_FILES+=usr/share/zoneinfo/America/Indiana/Indianapolis OLD_FILES+=usr/share/zoneinfo/America/Indiana/Knox OLD_FILES+=usr/share/zoneinfo/America/Indiana/Marengo OLD_FILES+=usr/share/zoneinfo/America/Indiana/Petersburg OLD_FILES+=usr/share/zoneinfo/America/Indiana/Tell_City OLD_FILES+=usr/share/zoneinfo/America/Indiana/Vevay OLD_FILES+=usr/share/zoneinfo/America/Indiana/Vincennes OLD_FILES+=usr/share/zoneinfo/America/Indiana/Winamac OLD_FILES+=usr/share/zoneinfo/America/Inuvik OLD_FILES+=usr/share/zoneinfo/America/Iqaluit OLD_FILES+=usr/share/zoneinfo/America/Jamaica OLD_FILES+=usr/share/zoneinfo/America/Juneau OLD_FILES+=usr/share/zoneinfo/America/Kentucky/Louisville OLD_FILES+=usr/share/zoneinfo/America/Kentucky/Monticello OLD_FILES+=usr/share/zoneinfo/America/Kralendijk OLD_FILES+=usr/share/zoneinfo/America/La_Paz OLD_FILES+=usr/share/zoneinfo/America/Lima OLD_FILES+=usr/share/zoneinfo/America/Los_Angeles OLD_FILES+=usr/share/zoneinfo/America/Lower_Princes OLD_FILES+=usr/share/zoneinfo/America/Maceio OLD_FILES+=usr/share/zoneinfo/America/Managua OLD_FILES+=usr/share/zoneinfo/America/Manaus OLD_FILES+=usr/share/zoneinfo/America/Marigot OLD_FILES+=usr/share/zoneinfo/America/Martinique OLD_FILES+=usr/share/zoneinfo/America/Matamoros OLD_FILES+=usr/share/zoneinfo/America/Mazatlan OLD_FILES+=usr/share/zoneinfo/America/Menominee OLD_FILES+=usr/share/zoneinfo/America/Merida OLD_FILES+=usr/share/zoneinfo/America/Metlakatla OLD_FILES+=usr/share/zoneinfo/America/Mexico_City OLD_FILES+=usr/share/zoneinfo/America/Miquelon OLD_FILES+=usr/share/zoneinfo/America/Moncton OLD_FILES+=usr/share/zoneinfo/America/Monterrey OLD_FILES+=usr/share/zoneinfo/America/Montevideo OLD_FILES+=usr/share/zoneinfo/America/Montreal OLD_FILES+=usr/share/zoneinfo/America/Montserrat OLD_FILES+=usr/share/zoneinfo/America/Nassau OLD_FILES+=usr/share/zoneinfo/America/New_York OLD_FILES+=usr/share/zoneinfo/America/Nipigon OLD_FILES+=usr/share/zoneinfo/America/Nome OLD_FILES+=usr/share/zoneinfo/America/Noronha OLD_FILES+=usr/share/zoneinfo/America/North_Dakota/Beulah OLD_FILES+=usr/share/zoneinfo/America/North_Dakota/Center OLD_FILES+=usr/share/zoneinfo/America/North_Dakota/New_Salem OLD_FILES+=usr/share/zoneinfo/America/Ojinaga OLD_FILES+=usr/share/zoneinfo/America/Panama OLD_FILES+=usr/share/zoneinfo/America/Pangnirtung OLD_FILES+=usr/share/zoneinfo/America/Paramaribo OLD_FILES+=usr/share/zoneinfo/America/Phoenix OLD_FILES+=usr/share/zoneinfo/America/Port-au-Prince OLD_FILES+=usr/share/zoneinfo/America/Port_of_Spain OLD_FILES+=usr/share/zoneinfo/America/Porto_Velho OLD_FILES+=usr/share/zoneinfo/America/Puerto_Rico OLD_FILES+=usr/share/zoneinfo/America/Rainy_River OLD_FILES+=usr/share/zoneinfo/America/Rankin_Inlet OLD_FILES+=usr/share/zoneinfo/America/Recife OLD_FILES+=usr/share/zoneinfo/America/Regina OLD_FILES+=usr/share/zoneinfo/America/Resolute OLD_FILES+=usr/share/zoneinfo/America/Rio_Branco OLD_FILES+=usr/share/zoneinfo/America/Santa_Isabel OLD_FILES+=usr/share/zoneinfo/America/Santarem OLD_FILES+=usr/share/zoneinfo/America/Santiago OLD_FILES+=usr/share/zoneinfo/America/Santo_Domingo OLD_FILES+=usr/share/zoneinfo/America/Sao_Paulo OLD_FILES+=usr/share/zoneinfo/America/Scoresbysund OLD_FILES+=usr/share/zoneinfo/America/Sitka OLD_FILES+=usr/share/zoneinfo/America/St_Barthelemy OLD_FILES+=usr/share/zoneinfo/America/St_Johns OLD_FILES+=usr/share/zoneinfo/America/St_Kitts OLD_FILES+=usr/share/zoneinfo/America/St_Lucia OLD_FILES+=usr/share/zoneinfo/America/St_Thomas OLD_FILES+=usr/share/zoneinfo/America/St_Vincent OLD_FILES+=usr/share/zoneinfo/America/Swift_Current OLD_FILES+=usr/share/zoneinfo/America/Tegucigalpa OLD_FILES+=usr/share/zoneinfo/America/Thule OLD_FILES+=usr/share/zoneinfo/America/Thunder_Bay OLD_FILES+=usr/share/zoneinfo/America/Tijuana OLD_FILES+=usr/share/zoneinfo/America/Toronto OLD_FILES+=usr/share/zoneinfo/America/Tortola OLD_FILES+=usr/share/zoneinfo/America/Vancouver OLD_FILES+=usr/share/zoneinfo/America/Whitehorse OLD_FILES+=usr/share/zoneinfo/America/Winnipeg OLD_FILES+=usr/share/zoneinfo/America/Yakutat OLD_FILES+=usr/share/zoneinfo/America/Yellowknife OLD_FILES+=usr/share/zoneinfo/Antarctica/Casey OLD_FILES+=usr/share/zoneinfo/Antarctica/Davis OLD_FILES+=usr/share/zoneinfo/Antarctica/DumontDUrville OLD_FILES+=usr/share/zoneinfo/Antarctica/Macquarie OLD_FILES+=usr/share/zoneinfo/Antarctica/Mawson OLD_FILES+=usr/share/zoneinfo/Antarctica/McMurdo OLD_FILES+=usr/share/zoneinfo/Antarctica/Palmer OLD_FILES+=usr/share/zoneinfo/Antarctica/Rothera OLD_FILES+=usr/share/zoneinfo/Antarctica/Syowa OLD_FILES+=usr/share/zoneinfo/Antarctica/Troll OLD_FILES+=usr/share/zoneinfo/Antarctica/Vostok OLD_FILES+=usr/share/zoneinfo/Arctic/Longyearbyen OLD_FILES+=usr/share/zoneinfo/Asia/Aden OLD_FILES+=usr/share/zoneinfo/Asia/Almaty OLD_FILES+=usr/share/zoneinfo/Asia/Amman OLD_FILES+=usr/share/zoneinfo/Asia/Anadyr OLD_FILES+=usr/share/zoneinfo/Asia/Aqtau OLD_FILES+=usr/share/zoneinfo/Asia/Aqtobe OLD_FILES+=usr/share/zoneinfo/Asia/Ashgabat OLD_FILES+=usr/share/zoneinfo/Asia/Baghdad OLD_FILES+=usr/share/zoneinfo/Asia/Bahrain OLD_FILES+=usr/share/zoneinfo/Asia/Baku OLD_FILES+=usr/share/zoneinfo/Asia/Bangkok OLD_FILES+=usr/share/zoneinfo/Asia/Beirut OLD_FILES+=usr/share/zoneinfo/Asia/Bishkek OLD_FILES+=usr/share/zoneinfo/Asia/Brunei OLD_FILES+=usr/share/zoneinfo/Asia/Chita OLD_FILES+=usr/share/zoneinfo/Asia/Choibalsan OLD_FILES+=usr/share/zoneinfo/Asia/Colombo OLD_FILES+=usr/share/zoneinfo/Asia/Damascus OLD_FILES+=usr/share/zoneinfo/Asia/Dhaka OLD_FILES+=usr/share/zoneinfo/Asia/Dili OLD_FILES+=usr/share/zoneinfo/Asia/Dubai OLD_FILES+=usr/share/zoneinfo/Asia/Dushanbe OLD_FILES+=usr/share/zoneinfo/Asia/Gaza OLD_FILES+=usr/share/zoneinfo/Asia/Hebron OLD_FILES+=usr/share/zoneinfo/Asia/Ho_Chi_Minh OLD_FILES+=usr/share/zoneinfo/Asia/Hong_Kong OLD_FILES+=usr/share/zoneinfo/Asia/Hovd OLD_FILES+=usr/share/zoneinfo/Asia/Irkutsk OLD_FILES+=usr/share/zoneinfo/Asia/Istanbul OLD_FILES+=usr/share/zoneinfo/Asia/Jakarta OLD_FILES+=usr/share/zoneinfo/Asia/Jayapura OLD_FILES+=usr/share/zoneinfo/Asia/Jerusalem OLD_FILES+=usr/share/zoneinfo/Asia/Kabul OLD_FILES+=usr/share/zoneinfo/Asia/Kamchatka OLD_FILES+=usr/share/zoneinfo/Asia/Karachi OLD_FILES+=usr/share/zoneinfo/Asia/Kathmandu OLD_FILES+=usr/share/zoneinfo/Asia/Khandyga OLD_FILES+=usr/share/zoneinfo/Asia/Kolkata OLD_FILES+=usr/share/zoneinfo/Asia/Krasnoyarsk OLD_FILES+=usr/share/zoneinfo/Asia/Kuala_Lumpur OLD_FILES+=usr/share/zoneinfo/Asia/Kuching OLD_FILES+=usr/share/zoneinfo/Asia/Kuwait OLD_FILES+=usr/share/zoneinfo/Asia/Macau OLD_FILES+=usr/share/zoneinfo/Asia/Magadan OLD_FILES+=usr/share/zoneinfo/Asia/Makassar OLD_FILES+=usr/share/zoneinfo/Asia/Manila OLD_FILES+=usr/share/zoneinfo/Asia/Muscat OLD_FILES+=usr/share/zoneinfo/Asia/Nicosia OLD_FILES+=usr/share/zoneinfo/Asia/Novokuznetsk OLD_FILES+=usr/share/zoneinfo/Asia/Novosibirsk OLD_FILES+=usr/share/zoneinfo/Asia/Omsk OLD_FILES+=usr/share/zoneinfo/Asia/Oral OLD_FILES+=usr/share/zoneinfo/Asia/Phnom_Penh OLD_FILES+=usr/share/zoneinfo/Asia/Pontianak OLD_FILES+=usr/share/zoneinfo/Asia/Pyongyang OLD_FILES+=usr/share/zoneinfo/Asia/Qatar OLD_FILES+=usr/share/zoneinfo/Asia/Qyzylorda OLD_FILES+=usr/share/zoneinfo/Asia/Rangoon OLD_FILES+=usr/share/zoneinfo/Asia/Riyadh OLD_FILES+=usr/share/zoneinfo/Asia/Sakhalin OLD_FILES+=usr/share/zoneinfo/Asia/Samarkand OLD_FILES+=usr/share/zoneinfo/Asia/Seoul OLD_FILES+=usr/share/zoneinfo/Asia/Shanghai OLD_FILES+=usr/share/zoneinfo/Asia/Singapore OLD_FILES+=usr/share/zoneinfo/Asia/Srednekolymsk OLD_FILES+=usr/share/zoneinfo/Asia/Taipei OLD_FILES+=usr/share/zoneinfo/Asia/Tashkent OLD_FILES+=usr/share/zoneinfo/Asia/Tbilisi OLD_FILES+=usr/share/zoneinfo/Asia/Tehran OLD_FILES+=usr/share/zoneinfo/Asia/Thimphu OLD_FILES+=usr/share/zoneinfo/Asia/Tokyo OLD_FILES+=usr/share/zoneinfo/Asia/Ulaanbaatar OLD_FILES+=usr/share/zoneinfo/Asia/Urumqi OLD_FILES+=usr/share/zoneinfo/Asia/Ust-Nera OLD_FILES+=usr/share/zoneinfo/Asia/Vientiane OLD_FILES+=usr/share/zoneinfo/Asia/Vladivostok OLD_FILES+=usr/share/zoneinfo/Asia/Yakutsk OLD_FILES+=usr/share/zoneinfo/Asia/Yekaterinburg OLD_FILES+=usr/share/zoneinfo/Asia/Yerevan OLD_FILES+=usr/share/zoneinfo/Atlantic/Azores OLD_FILES+=usr/share/zoneinfo/Atlantic/Bermuda OLD_FILES+=usr/share/zoneinfo/Atlantic/Canary OLD_FILES+=usr/share/zoneinfo/Atlantic/Cape_Verde OLD_FILES+=usr/share/zoneinfo/Atlantic/Faroe OLD_FILES+=usr/share/zoneinfo/Atlantic/Madeira OLD_FILES+=usr/share/zoneinfo/Atlantic/Reykjavik OLD_FILES+=usr/share/zoneinfo/Atlantic/South_Georgia OLD_FILES+=usr/share/zoneinfo/Atlantic/St_Helena OLD_FILES+=usr/share/zoneinfo/Atlantic/Stanley OLD_FILES+=usr/share/zoneinfo/Australia/Adelaide OLD_FILES+=usr/share/zoneinfo/Australia/Brisbane OLD_FILES+=usr/share/zoneinfo/Australia/Broken_Hill OLD_FILES+=usr/share/zoneinfo/Australia/Currie OLD_FILES+=usr/share/zoneinfo/Australia/Darwin OLD_FILES+=usr/share/zoneinfo/Australia/Eucla OLD_FILES+=usr/share/zoneinfo/Australia/Hobart OLD_FILES+=usr/share/zoneinfo/Australia/Lindeman OLD_FILES+=usr/share/zoneinfo/Australia/Lord_Howe OLD_FILES+=usr/share/zoneinfo/Australia/Melbourne OLD_FILES+=usr/share/zoneinfo/Australia/Perth OLD_FILES+=usr/share/zoneinfo/Australia/Sydney OLD_FILES+=usr/share/zoneinfo/CET OLD_FILES+=usr/share/zoneinfo/CST6CDT OLD_FILES+=usr/share/zoneinfo/EET OLD_FILES+=usr/share/zoneinfo/EST OLD_FILES+=usr/share/zoneinfo/EST5EDT OLD_FILES+=usr/share/zoneinfo/Etc/GMT OLD_FILES+=usr/share/zoneinfo/Etc/GMT+0 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+1 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+10 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+11 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+12 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+2 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+3 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+4 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+5 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+6 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+7 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+8 OLD_FILES+=usr/share/zoneinfo/Etc/GMT+9 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-0 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-1 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-10 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-11 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-12 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-13 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-14 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-2 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-3 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-4 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-5 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-6 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-7 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-8 OLD_FILES+=usr/share/zoneinfo/Etc/GMT-9 OLD_FILES+=usr/share/zoneinfo/Etc/GMT0 OLD_FILES+=usr/share/zoneinfo/Etc/Greenwich OLD_FILES+=usr/share/zoneinfo/Etc/UCT OLD_FILES+=usr/share/zoneinfo/Etc/UTC OLD_FILES+=usr/share/zoneinfo/Etc/Universal OLD_FILES+=usr/share/zoneinfo/Etc/Zulu OLD_FILES+=usr/share/zoneinfo/Europe/Amsterdam OLD_FILES+=usr/share/zoneinfo/Europe/Andorra OLD_FILES+=usr/share/zoneinfo/Europe/Athens OLD_FILES+=usr/share/zoneinfo/Europe/Belgrade OLD_FILES+=usr/share/zoneinfo/Europe/Berlin OLD_FILES+=usr/share/zoneinfo/Europe/Bratislava OLD_FILES+=usr/share/zoneinfo/Europe/Brussels OLD_FILES+=usr/share/zoneinfo/Europe/Bucharest OLD_FILES+=usr/share/zoneinfo/Europe/Budapest OLD_FILES+=usr/share/zoneinfo/Europe/Busingen OLD_FILES+=usr/share/zoneinfo/Europe/Chisinau OLD_FILES+=usr/share/zoneinfo/Europe/Copenhagen OLD_FILES+=usr/share/zoneinfo/Europe/Dublin OLD_FILES+=usr/share/zoneinfo/Europe/Gibraltar OLD_FILES+=usr/share/zoneinfo/Europe/Guernsey OLD_FILES+=usr/share/zoneinfo/Europe/Helsinki OLD_FILES+=usr/share/zoneinfo/Europe/Isle_of_Man OLD_FILES+=usr/share/zoneinfo/Europe/Istanbul OLD_FILES+=usr/share/zoneinfo/Europe/Jersey OLD_FILES+=usr/share/zoneinfo/Europe/Kaliningrad OLD_FILES+=usr/share/zoneinfo/Europe/Kiev OLD_FILES+=usr/share/zoneinfo/Europe/Lisbon OLD_FILES+=usr/share/zoneinfo/Europe/Ljubljana OLD_FILES+=usr/share/zoneinfo/Europe/London OLD_FILES+=usr/share/zoneinfo/Europe/Luxembourg OLD_FILES+=usr/share/zoneinfo/Europe/Madrid OLD_FILES+=usr/share/zoneinfo/Europe/Malta OLD_FILES+=usr/share/zoneinfo/Europe/Mariehamn OLD_FILES+=usr/share/zoneinfo/Europe/Minsk OLD_FILES+=usr/share/zoneinfo/Europe/Monaco OLD_FILES+=usr/share/zoneinfo/Europe/Moscow OLD_FILES+=usr/share/zoneinfo/Europe/Nicosia OLD_FILES+=usr/share/zoneinfo/Europe/Oslo OLD_FILES+=usr/share/zoneinfo/Europe/Paris OLD_FILES+=usr/share/zoneinfo/Europe/Podgorica OLD_FILES+=usr/share/zoneinfo/Europe/Prague OLD_FILES+=usr/share/zoneinfo/Europe/Riga OLD_FILES+=usr/share/zoneinfo/Europe/Rome OLD_FILES+=usr/share/zoneinfo/Europe/Samara OLD_FILES+=usr/share/zoneinfo/Europe/San_Marino OLD_FILES+=usr/share/zoneinfo/Europe/Sarajevo OLD_FILES+=usr/share/zoneinfo/Europe/Simferopol OLD_FILES+=usr/share/zoneinfo/Europe/Skopje OLD_FILES+=usr/share/zoneinfo/Europe/Sofia OLD_FILES+=usr/share/zoneinfo/Europe/Stockholm OLD_FILES+=usr/share/zoneinfo/Europe/Tallinn OLD_FILES+=usr/share/zoneinfo/Europe/Tirane OLD_FILES+=usr/share/zoneinfo/Europe/Uzhgorod OLD_FILES+=usr/share/zoneinfo/Europe/Vaduz OLD_FILES+=usr/share/zoneinfo/Europe/Vatican OLD_FILES+=usr/share/zoneinfo/Europe/Vienna OLD_FILES+=usr/share/zoneinfo/Europe/Vilnius OLD_FILES+=usr/share/zoneinfo/Europe/Volgograd OLD_FILES+=usr/share/zoneinfo/Europe/Warsaw OLD_FILES+=usr/share/zoneinfo/Europe/Zagreb OLD_FILES+=usr/share/zoneinfo/Europe/Zaporozhye OLD_FILES+=usr/share/zoneinfo/Europe/Zurich OLD_FILES+=usr/share/zoneinfo/Factory OLD_FILES+=usr/share/zoneinfo/HST OLD_FILES+=usr/share/zoneinfo/Indian/Antananarivo OLD_FILES+=usr/share/zoneinfo/Indian/Chagos OLD_FILES+=usr/share/zoneinfo/Indian/Christmas OLD_FILES+=usr/share/zoneinfo/Indian/Cocos OLD_FILES+=usr/share/zoneinfo/Indian/Comoro OLD_FILES+=usr/share/zoneinfo/Indian/Kerguelen OLD_FILES+=usr/share/zoneinfo/Indian/Mahe OLD_FILES+=usr/share/zoneinfo/Indian/Maldives OLD_FILES+=usr/share/zoneinfo/Indian/Mauritius OLD_FILES+=usr/share/zoneinfo/Indian/Mayotte OLD_FILES+=usr/share/zoneinfo/Indian/Reunion OLD_FILES+=usr/share/zoneinfo/MET OLD_FILES+=usr/share/zoneinfo/MST OLD_FILES+=usr/share/zoneinfo/MST7MDT OLD_FILES+=usr/share/zoneinfo/PST8PDT OLD_FILES+=usr/share/zoneinfo/Pacific/Apia OLD_FILES+=usr/share/zoneinfo/Pacific/Auckland OLD_FILES+=usr/share/zoneinfo/Pacific/Bougainville OLD_FILES+=usr/share/zoneinfo/Pacific/Chatham OLD_FILES+=usr/share/zoneinfo/Pacific/Chuuk OLD_FILES+=usr/share/zoneinfo/Pacific/Easter OLD_FILES+=usr/share/zoneinfo/Pacific/Efate OLD_FILES+=usr/share/zoneinfo/Pacific/Enderbury OLD_FILES+=usr/share/zoneinfo/Pacific/Fakaofo OLD_FILES+=usr/share/zoneinfo/Pacific/Fiji OLD_FILES+=usr/share/zoneinfo/Pacific/Funafuti OLD_FILES+=usr/share/zoneinfo/Pacific/Galapagos OLD_FILES+=usr/share/zoneinfo/Pacific/Gambier OLD_FILES+=usr/share/zoneinfo/Pacific/Guadalcanal OLD_FILES+=usr/share/zoneinfo/Pacific/Guam OLD_FILES+=usr/share/zoneinfo/Pacific/Honolulu OLD_FILES+=usr/share/zoneinfo/Pacific/Johnston OLD_FILES+=usr/share/zoneinfo/Pacific/Kiritimati OLD_FILES+=usr/share/zoneinfo/Pacific/Kosrae OLD_FILES+=usr/share/zoneinfo/Pacific/Kwajalein OLD_FILES+=usr/share/zoneinfo/Pacific/Majuro OLD_FILES+=usr/share/zoneinfo/Pacific/Marquesas OLD_FILES+=usr/share/zoneinfo/Pacific/Midway OLD_FILES+=usr/share/zoneinfo/Pacific/Nauru OLD_FILES+=usr/share/zoneinfo/Pacific/Niue OLD_FILES+=usr/share/zoneinfo/Pacific/Norfolk OLD_FILES+=usr/share/zoneinfo/Pacific/Noumea OLD_FILES+=usr/share/zoneinfo/Pacific/Pago_Pago OLD_FILES+=usr/share/zoneinfo/Pacific/Palau OLD_FILES+=usr/share/zoneinfo/Pacific/Pitcairn OLD_FILES+=usr/share/zoneinfo/Pacific/Pohnpei OLD_FILES+=usr/share/zoneinfo/Pacific/Port_Moresby OLD_FILES+=usr/share/zoneinfo/Pacific/Rarotonga OLD_FILES+=usr/share/zoneinfo/Pacific/Saipan OLD_FILES+=usr/share/zoneinfo/Pacific/Tahiti OLD_FILES+=usr/share/zoneinfo/Pacific/Tarawa OLD_FILES+=usr/share/zoneinfo/Pacific/Tongatapu OLD_FILES+=usr/share/zoneinfo/Pacific/Wake OLD_FILES+=usr/share/zoneinfo/Pacific/Wallis OLD_FILES+=usr/share/zoneinfo/UTC OLD_FILES+=usr/share/zoneinfo/WET OLD_FILES+=usr/share/zoneinfo/posixrules OLD_FILES+=usr/share/zoneinfo/zone.tab OLD_DIRS+=usr/share/zoneinfo/Africa OLD_DIRS+=usr/share/zoneinfo/America/Argentina OLD_DIRS+=usr/share/zoneinfo/America/Indiana OLD_DIRS+=usr/share/zoneinfo/America/Kentucky OLD_DIRS+=usr/share/zoneinfo/America/North_Dakota OLD_DIRS+=usr/share/zoneinfo/America OLD_DIRS+=usr/share/zoneinfo/Antarctica OLD_DIRS+=usr/share/zoneinfo/Arctic OLD_DIRS+=usr/share/zoneinfo/Asia OLD_DIRS+=usr/share/zoneinfo/Atlantic OLD_DIRS+=usr/share/zoneinfo/Australia OLD_DIRS+=usr/share/zoneinfo/Etc OLD_DIRS+=usr/share/zoneinfo/Europe OLD_DIRS+=usr/share/zoneinfo/Indian OLD_DIRS+=usr/share/zoneinfo/Pacific OLD_DIRS+=usr/share/zoneinfo/SystemV OLD_DIRS+=usr/share/zoneinfo .endif .if ${MK_SVNLITE} == no || ${MK_SVN} == yes OLD_FILES+=usr/bin/svnlite OLD_FILES+=usr/bin/svnliteadmin OLD_FILES+=usr/bin/svnlitedumpfilter OLD_FILES+=usr/bin/svnlitelook OLD_FILES+=usr/bin/svnlitemucc OLD_FILES+=usr/bin/svnliterdump OLD_FILES+=usr/bin/svnliteserve OLD_FILES+=usr/bin/svnlitesync OLD_FILES+=usr/bin/svnliteversion OLD_FILES+=usr/share/man/man1/svnlite.1.gz .endif .if ${MK_SVN} == no OLD_FILES+=usr/bin/svn OLD_FILES+=usr/bin/svnadmin OLD_FILES+=usr/bin/svndumpfilter OLD_FILES+=usr/bin/svnlook OLD_FILES+=usr/bin/svnmucc OLD_FILES+=usr/bin/svnrdump OLD_FILES+=usr/bin/svnserve OLD_FILES+=usr/bin/svnsync OLD_FILES+=usr/bin/svnversion .endif .if ${MK_HYPERV} == no OLD_FILES+=etc/devd/hyperv.conf OLD_FILES+=usr/libexec/hyperv/hv_get_dhcp_info OLD_FILES+=usr/libexec/hyperv/hv_get_dns_info OLD_FILES+=usr/libexec/hyperv/hv_set_ifconfig OLD_FILES+=usr/sbin/hv_kvp_daemon OLD_FILES+=usr/share/man/man8/hv_kvp_daemon.8.gz OLD_DIRS+=usr/libexec/hyperv .endif Index: projects/building-blocks/usr.sbin/ctladm/ctladm.c =================================================================== --- projects/building-blocks/usr.sbin/ctladm/ctladm.c (revision 278195) +++ projects/building-blocks/usr.sbin/ctladm/ctladm.c (revision 278196) @@ -1,4929 +1,4930 @@ /*- * Copyright (c) 2003, 2004 Silicon Graphics International Corp. * Copyright (c) 1997-2007 Kenneth D. Merry * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala * 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, * 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. * * $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.c#4 $ */ /* * CAM Target Layer exercise program. * * Author: Ken Merry */ #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 "ctladm.h" #ifdef min #undef min #endif #define min(x,y) (x < y) ? x : y typedef enum { CTLADM_CMD_TUR, CTLADM_CMD_INQUIRY, CTLADM_CMD_REQ_SENSE, CTLADM_CMD_ARRAYLIST, CTLADM_CMD_REPORT_LUNS, CTLADM_CMD_HELP, CTLADM_CMD_DEVLIST, CTLADM_CMD_ADDDEV, CTLADM_CMD_RM, CTLADM_CMD_CREATE, CTLADM_CMD_READ, CTLADM_CMD_WRITE, CTLADM_CMD_PORT, CTLADM_CMD_PORTLIST, CTLADM_CMD_READCAPACITY, CTLADM_CMD_MODESENSE, CTLADM_CMD_DUMPOOA, CTLADM_CMD_DUMPSTRUCTS, CTLADM_CMD_START, CTLADM_CMD_STOP, CTLADM_CMD_SYNC_CACHE, CTLADM_CMD_SHUTDOWN, CTLADM_CMD_STARTUP, CTLADM_CMD_LUNLIST, CTLADM_CMD_HARDSTOP, CTLADM_CMD_HARDSTART, CTLADM_CMD_DELAY, CTLADM_CMD_REALSYNC, CTLADM_CMD_SETSYNC, CTLADM_CMD_GETSYNC, CTLADM_CMD_ERR_INJECT, CTLADM_CMD_BBRREAD, CTLADM_CMD_PRES_IN, CTLADM_CMD_PRES_OUT, CTLADM_CMD_INQ_VPD_DEVID, CTLADM_CMD_RTPG, CTLADM_CMD_MODIFY, CTLADM_CMD_ISLIST, CTLADM_CMD_ISLOGOUT, CTLADM_CMD_ISTERMINATE, CTLADM_CMD_LUNMAP } ctladm_cmdfunction; typedef enum { CTLADM_ARG_NONE = 0x0000000, CTLADM_ARG_AUTOSENSE = 0x0000001, CTLADM_ARG_DEVICE = 0x0000002, CTLADM_ARG_ARRAYSIZE = 0x0000004, CTLADM_ARG_BACKEND = 0x0000008, CTLADM_ARG_CDBSIZE = 0x0000010, CTLADM_ARG_DATALEN = 0x0000020, CTLADM_ARG_FILENAME = 0x0000040, CTLADM_ARG_LBA = 0x0000080, CTLADM_ARG_PC = 0x0000100, CTLADM_ARG_PAGE_CODE = 0x0000200, CTLADM_ARG_PAGE_LIST = 0x0000400, CTLADM_ARG_SUBPAGE = 0x0000800, CTLADM_ARG_PAGELIST = 0x0001000, CTLADM_ARG_DBD = 0x0002000, CTLADM_ARG_TARG_LUN = 0x0004000, CTLADM_ARG_BLOCKSIZE = 0x0008000, CTLADM_ARG_IMMED = 0x0010000, CTLADM_ARG_RELADR = 0x0020000, CTLADM_ARG_RETRIES = 0x0040000, CTLADM_ARG_ONOFFLINE = 0x0080000, CTLADM_ARG_ONESHOT = 0x0100000, CTLADM_ARG_TIMEOUT = 0x0200000, CTLADM_ARG_INITIATOR = 0x0400000, CTLADM_ARG_NOCOPY = 0x0800000, CTLADM_ARG_NEED_TL = 0x1000000 } ctladm_cmdargs; struct ctladm_opts { const char *optname; uint32_t cmdnum; ctladm_cmdargs argnum; const char *subopt; }; typedef enum { CC_OR_NOT_FOUND, CC_OR_AMBIGUOUS, CC_OR_FOUND } ctladm_optret; static const char rw_opts[] = "Nb:c:d:f:l:"; static const char startstop_opts[] = "io"; static struct ctladm_opts option_table[] = { {"adddev", CTLADM_CMD_ADDDEV, CTLADM_ARG_NONE, NULL}, {"bbrread", CTLADM_CMD_BBRREAD, CTLADM_ARG_NEED_TL, "d:l:"}, {"create", CTLADM_CMD_CREATE, CTLADM_ARG_NONE, "b:B:d:l:o:s:S:t:"}, {"delay", CTLADM_CMD_DELAY, CTLADM_ARG_NEED_TL, "T:l:t:"}, {"devid", CTLADM_CMD_INQ_VPD_DEVID, CTLADM_ARG_NEED_TL, NULL}, {"devlist", CTLADM_CMD_DEVLIST, CTLADM_ARG_NONE, "b:vx"}, {"dumpooa", CTLADM_CMD_DUMPOOA, CTLADM_ARG_NONE, NULL}, {"dumpstructs", CTLADM_CMD_DUMPSTRUCTS, CTLADM_ARG_NONE, NULL}, {"getsync", CTLADM_CMD_GETSYNC, CTLADM_ARG_NEED_TL, NULL}, {"hardstart", CTLADM_CMD_HARDSTART, CTLADM_ARG_NONE, NULL}, {"hardstop", CTLADM_CMD_HARDSTOP, CTLADM_ARG_NONE, NULL}, {"help", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL}, {"inject", CTLADM_CMD_ERR_INJECT, CTLADM_ARG_NEED_TL, "cd:i:p:r:s:"}, {"inquiry", CTLADM_CMD_INQUIRY, CTLADM_ARG_NEED_TL, NULL}, {"islist", CTLADM_CMD_ISLIST, CTLADM_ARG_NONE, "vx"}, {"islogout", CTLADM_CMD_ISLOGOUT, CTLADM_ARG_NONE, "ac:i:p:"}, {"isterminate", CTLADM_CMD_ISTERMINATE, CTLADM_ARG_NONE, "ac:i:p:"}, {"lunlist", CTLADM_CMD_LUNLIST, CTLADM_ARG_NONE, NULL}, {"lunmap", CTLADM_CMD_LUNMAP, CTLADM_ARG_NONE, "p:l:L:"}, {"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"}, {"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:s:"}, {"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:p:qt:w:W:x"}, {"portlist", CTLADM_CMD_PORTLIST, CTLADM_ARG_NONE, "f:ilp:qvx"}, {"prin", CTLADM_CMD_PRES_IN, CTLADM_ARG_NEED_TL, "a:"}, {"prout", CTLADM_CMD_PRES_OUT, CTLADM_ARG_NEED_TL, "a:k:r:s:"}, {"read", CTLADM_CMD_READ, CTLADM_ARG_NEED_TL, rw_opts}, {"readcapacity", CTLADM_CMD_READCAPACITY, CTLADM_ARG_NEED_TL, "c:"}, {"realsync", CTLADM_CMD_REALSYNC, CTLADM_ARG_NONE, NULL}, {"remove", CTLADM_CMD_RM, CTLADM_ARG_NONE, "b:l:o:"}, {"reportluns", CTLADM_CMD_REPORT_LUNS, CTLADM_ARG_NEED_TL, NULL}, {"reqsense", CTLADM_CMD_REQ_SENSE, CTLADM_ARG_NEED_TL, NULL}, {"rtpg", CTLADM_CMD_RTPG, CTLADM_ARG_NEED_TL, NULL}, {"setsync", CTLADM_CMD_SETSYNC, CTLADM_ARG_NEED_TL, "i:"}, {"shutdown", CTLADM_CMD_SHUTDOWN, CTLADM_ARG_NONE, NULL}, {"start", CTLADM_CMD_START, CTLADM_ARG_NEED_TL, startstop_opts}, {"startup", CTLADM_CMD_STARTUP, CTLADM_ARG_NONE, NULL}, {"stop", CTLADM_CMD_STOP, CTLADM_ARG_NEED_TL, startstop_opts}, {"synccache", CTLADM_CMD_SYNC_CACHE, CTLADM_ARG_NEED_TL, "b:c:il:r"}, {"tur", CTLADM_CMD_TUR, CTLADM_ARG_NEED_TL, NULL}, {"write", CTLADM_CMD_WRITE, CTLADM_ARG_NEED_TL, rw_opts}, {"-?", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL}, {"-h", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL}, {NULL, 0, 0, NULL} }; ctladm_optret getoption(struct ctladm_opts *table, char *arg, uint32_t *cmdnum, ctladm_cmdargs *argnum, const char **subopt); static int cctl_parse_tl(char *str, int *target, int *lun); static int cctl_dump_ooa(int fd, int argc, char **argv); static int cctl_port_dump(int fd, int quiet, int xml, int32_t fe_num, ctl_port_type port_type); static int cctl_port(int fd, int argc, char **argv, char *combinedopt); static int cctl_do_io(int fd, int retries, union ctl_io *io, const char *func); static int cctl_delay(int fd, int target, int lun, int argc, char **argv, char *combinedopt); static int cctl_lunlist(int fd); static void cctl_cfi_mt_statusstr(cfi_mt_status status, char *str, int str_len); static void cctl_cfi_bbr_statusstr(cfi_bbrread_status, char *str, int str_len); static int cctl_hardstopstart(int fd, ctladm_cmdfunction command); static int cctl_bbrread(int fd, int target, int lun, int iid, int argc, char **argv, char *combinedopt); static int cctl_startup_shutdown(int fd, int target, int lun, int iid, ctladm_cmdfunction command); static int cctl_sync_cache(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt); static int cctl_start_stop(int fd, int target, int lun, int iid, int retries, int start, int argc, char **argv, char *combinedopt); static int cctl_mode_sense(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt); static int cctl_read_capacity(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt); static int cctl_read_write(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt, ctladm_cmdfunction command); static int cctl_get_luns(int fd, int target, int lun, int iid, int retries, struct scsi_report_luns_data **lun_data, uint32_t *num_luns); static int cctl_report_luns(int fd, int target, int lun, int iid, int retries); static int cctl_tur(int fd, int target, int lun, int iid, int retries); static int cctl_get_inquiry(int fd, int target, int lun, int iid, int retries, char *path_str, int path_len, struct scsi_inquiry_data *inq_data); static int cctl_inquiry(int fd, int target, int lun, int iid, int retries); static int cctl_req_sense(int fd, int target, int lun, int iid, int retries); static int cctl_persistent_reserve_in(int fd, int target, int lun, int initiator, int argc, char **argv, char *combinedopt, int retry_count); static int cctl_persistent_reserve_out(int fd, int target, int lun, int initiator, int argc, char **argv, char *combinedopt, int retry_count); static int cctl_create_lun(int fd, int argc, char **argv, char *combinedopt); static int cctl_inquiry_vpd_devid(int fd, int target, int lun, int initiator); static int cctl_report_target_port_group(int fd, int target, int lun, int initiator); static int cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt); ctladm_optret getoption(struct ctladm_opts *table, char *arg, uint32_t *cmdnum, ctladm_cmdargs *argnum, const char **subopt) { struct ctladm_opts *opts; int num_matches = 0; for (opts = table; (opts != NULL) && (opts->optname != NULL); opts++) { if (strncmp(opts->optname, arg, strlen(arg)) == 0) { *cmdnum = opts->cmdnum; *argnum = opts->argnum; *subopt = opts->subopt; if (strcmp(opts->optname, arg) == 0) return (CC_OR_FOUND); if (++num_matches > 1) return(CC_OR_AMBIGUOUS); } } if (num_matches > 0) return(CC_OR_FOUND); else return(CC_OR_NOT_FOUND); } static int cctl_parse_tl(char *str, int *target, int *lun) { char *tmpstr; int retval; retval = 0; while (isspace(*str) && (*str != '\0')) str++; tmpstr = (char *)strtok(str, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *target = strtol(tmpstr, NULL, 0); tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *lun = strtol(tmpstr, NULL, 0); } else retval = -1; } else retval = -1; return (retval); } static int cctl_dump_ooa(int fd, int argc, char **argv) { struct ctl_ooa ooa; long double cmd_latency; int num_entries, len; int target = -1, lun = -1; int retval; unsigned int i; num_entries = 104; if ((argc > 2) && (isdigit(argv[2][0]))) { retval = cctl_parse_tl(argv[2], &target, &lun); if (retval != 0) warnx("invalid target:lun argument %s", argv[2]); } retry: len = num_entries * sizeof(struct ctl_ooa_entry); bzero(&ooa, sizeof(ooa)); ooa.entries = malloc(len); if (ooa.entries == NULL) { warn("%s: error mallocing %d bytes", __func__, len); return (1); } if (argc > 2) { ooa.lun_num = lun; } else ooa.flags |= CTL_OOA_FLAG_ALL_LUNS; ooa.alloc_len = len; ooa.alloc_num = num_entries; if (ioctl(fd, CTL_GET_OOA, &ooa) == -1) { warn("%s: CTL_GET_OOA ioctl failed", __func__); retval = 1; goto bailout; } if (ooa.status == CTL_OOA_NEED_MORE_SPACE) { num_entries = num_entries * 2; free(ooa.entries); ooa.entries = NULL; goto retry; } if (ooa.status != CTL_OOA_OK) { warnx("%s: CTL_GET_OOA ioctl returned error %d", __func__, ooa.status); retval = 1; goto bailout; } fprintf(stdout, "Dumping OOA queues\n"); for (i = 0; i < ooa.fill_num; i++) { struct ctl_ooa_entry *entry; char cdb_str[(SCSI_MAX_CDBLEN * 3) +1]; struct bintime delta_bt; struct timespec ts; entry = &ooa.entries[i]; delta_bt = ooa.cur_bt; bintime_sub(&delta_bt, &entry->start_bt); bintime2timespec(&delta_bt, &ts); cmd_latency = ts.tv_sec * 1000; if (ts.tv_nsec > 0) cmd_latency += ts.tv_nsec / 1000000; fprintf(stdout, "LUN %jd tag 0x%04x%s%s%s%s%s: %s. CDB: %s " "(%0.0Lf ms)\n", (intmax_t)entry->lun_num, entry->tag_num, (entry->cmd_flags & CTL_OOACMD_FLAG_BLOCKED) ? " BLOCKED" : "", (entry->cmd_flags & CTL_OOACMD_FLAG_DMA) ? " DMA" : "", (entry->cmd_flags & CTL_OOACMD_FLAG_DMA_QUEUED) ? " DMAQUEUED" : "", (entry->cmd_flags & CTL_OOACMD_FLAG_ABORT) ? " ABORT" : "", (entry->cmd_flags & CTL_OOACMD_FLAG_RTR) ? " RTR" :"", scsi_op_desc(entry->cdb[0], NULL), scsi_cdb_string(entry->cdb, cdb_str, sizeof(cdb_str)), cmd_latency); } fprintf(stdout, "OOA queues dump done\n"); #if 0 if (ioctl(fd, CTL_DUMP_OOA) == -1) { warn("%s: CTL_DUMP_OOA ioctl failed", __func__); return (1); } #endif bailout: free(ooa.entries); return (0); } static int cctl_dump_structs(int fd, ctladm_cmdargs cmdargs __unused) { if (ioctl(fd, CTL_DUMP_STRUCTS) == -1) { warn(__func__); return (1); } return (0); } static int cctl_port_dump(int fd, int quiet, int xml, int32_t targ_port, ctl_port_type port_type) { struct ctl_port_list port_list; struct ctl_port_entry *entries; struct sbuf *sb = NULL; int num_entries; int did_print = 0; unsigned int i; num_entries = 16; retry: entries = malloc(sizeof(*entries) * num_entries); bzero(&port_list, sizeof(port_list)); port_list.entries = entries; port_list.alloc_num = num_entries; port_list.alloc_len = num_entries * sizeof(*entries); if (ioctl(fd, CTL_GET_PORT_LIST, &port_list) != 0) { warn("%s: CTL_GET_PORT_LIST ioctl failed", __func__); return (1); } if (port_list.status == CTL_PORT_LIST_NEED_MORE_SPACE) { printf("%s: allocated %d, need %d, retrying\n", __func__, num_entries, port_list.fill_num + port_list.dropped_num); free(entries); num_entries = port_list.fill_num + port_list.dropped_num; goto retry; } if ((quiet == 0) && (xml == 0)) printf("Port Online Type Name pp vp %-18s %-18s\n", "WWNN", "WWPN"); if (xml != 0) { sb = sbuf_new_auto(); sbuf_printf(sb, "\n"); } for (i = 0; i < port_list.fill_num; i++) { struct ctl_port_entry *entry; const char *type; entry = &entries[i]; switch (entry->port_type) { case CTL_PORT_FC: type = "FC"; break; case CTL_PORT_SCSI: type = "SCSI"; break; case CTL_PORT_IOCTL: type = "IOCTL"; break; case CTL_PORT_INTERNAL: type = "INTERNAL"; break; case CTL_PORT_ISC: type = "ISC"; break; case CTL_PORT_ISCSI: type = "ISCSI"; break; case CTL_PORT_SAS: type = "SAS"; break; default: type = "UNKNOWN"; break; } /* * If the user specified a frontend number or a particular * frontend type, only print out that particular frontend * or frontend type. */ if ((targ_port != -1) && (targ_port != entry->targ_port)) continue; else if ((port_type != CTL_PORT_NONE) && ((port_type & entry->port_type) == 0)) continue; did_print = 1; #if 0 printf("Num: %ju Type: %s (%#x) Name: %s Physical Port: %d " "Virtual Port: %d\n", (uintmax_t)entry->fe_num, type, entry->port_type, entry->fe_name, entry->physical_port, entry->virtual_port); printf("WWNN %#jx WWPN %#jx Online: %s\n", (uintmax_t)entry->wwnn, (uintmax_t)entry->wwpn, (entry->online) ? "YES" : "NO" ); #endif if (xml == 0) { printf("%-4d %-6s %-8s %-12s %-2d %-2d %#-18jx " "%#-18jx\n", entry->targ_port, (entry->online) ? "YES" : "NO", type, entry->port_name, entry->physical_port, entry->virtual_port, (uintmax_t)entry->wwnn, (uintmax_t)entry->wwpn); } else { sbuf_printf(sb, "\n", entry->targ_port); sbuf_printf(sb, "%s\n", (entry->online) ? "YES" : "NO"); sbuf_printf(sb, "%s\n", type); sbuf_printf(sb, "%s\n", entry->port_name); sbuf_printf(sb, "%d\n", entry->physical_port); sbuf_printf(sb, "%d\n", entry->virtual_port); sbuf_printf(sb, "%#jx\n", (uintmax_t)entry->wwnn); sbuf_printf(sb, "%#jx\n", (uintmax_t)entry->wwpn); sbuf_printf(sb, "\n"); } } if (xml != 0) { sbuf_printf(sb, "\n"); if (sbuf_finish(sb) != 0) err(1, "%s: sbuf_finish", __func__); printf("%s", sbuf_data(sb)); sbuf_delete(sb); } /* * Give some indication that we didn't find the frontend or * frontend type requested by the user. We could print something * out, but it would probably be better to hide that behind a * verbose flag. */ if ((did_print == 0) && ((targ_port != -1) || (port_type != CTL_PORT_NONE))) return (1); else return (0); } typedef enum { CCTL_PORT_MODE_NONE, CCTL_PORT_MODE_LIST, CCTL_PORT_MODE_SET, CCTL_PORT_MODE_ON, CCTL_PORT_MODE_OFF } cctl_port_mode; static struct ctladm_opts cctl_fe_table[] = { {"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL}, {"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL}, {"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL}, {"iscsi", CTL_PORT_ISCSI, CTLADM_ARG_NONE, NULL}, {"sas", CTL_PORT_SAS, CTLADM_ARG_NONE, NULL}, {"all", CTL_PORT_ALL, CTLADM_ARG_NONE, NULL}, {NULL, 0, 0, NULL} }; static int cctl_port(int fd, int argc, char **argv, char *combinedopt) { int c; int32_t targ_port = -1; int retval = 0; int wwnn_set = 0, wwpn_set = 0; uint64_t wwnn = 0, wwpn = 0; cctl_port_mode port_mode = CCTL_PORT_MODE_NONE; struct ctl_port_entry entry; ctl_port_type port_type = CTL_PORT_NONE; int quiet = 0, xml = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'l': if (port_mode != CCTL_PORT_MODE_NONE) goto bailout_badarg; port_mode = CCTL_PORT_MODE_LIST; break; case 'o': if (port_mode != CCTL_PORT_MODE_NONE) goto bailout_badarg; if (strcasecmp(optarg, "on") == 0) port_mode = CCTL_PORT_MODE_ON; else if (strcasecmp(optarg, "off") == 0) port_mode = CCTL_PORT_MODE_OFF; else { warnx("Invalid -o argument %s, \"on\" or " "\"off\" are the only valid args", optarg); retval = 1; goto bailout; } break; case 'p': targ_port = strtol(optarg, NULL, 0); break; case 'q': quiet = 1; break; case 't': { ctladm_optret optret; ctladm_cmdargs argnum; const char *subopt; ctl_port_type tmp_port_type; optret = getoption(cctl_fe_table, optarg, &tmp_port_type, &argnum, &subopt); if (optret == CC_OR_AMBIGUOUS) { warnx("%s: ambiguous frontend type %s", __func__, optarg); retval = 1; goto bailout; } else if (optret == CC_OR_NOT_FOUND) { warnx("%s: invalid frontend type %s", __func__, optarg); retval = 1; goto bailout; } port_type |= tmp_port_type; break; } case 'w': if ((port_mode != CCTL_PORT_MODE_NONE) && (port_mode != CCTL_PORT_MODE_SET)) goto bailout_badarg; port_mode = CCTL_PORT_MODE_SET; wwnn = strtoull(optarg, NULL, 0); wwnn_set = 1; break; case 'W': if ((port_mode != CCTL_PORT_MODE_NONE) && (port_mode != CCTL_PORT_MODE_SET)) goto bailout_badarg; port_mode = CCTL_PORT_MODE_SET; wwpn = strtoull(optarg, NULL, 0); wwpn_set = 1; break; case 'x': xml = 1; break; } } /* * The user can specify either one or more frontend types (-t), or * a specific frontend, but not both. * * If the user didn't specify a frontend type or number, set it to * all. This is primarily needed for the enable/disable ioctls. * This will be a no-op for the listing code. For the set ioctl, * we'll throw an error, since that only works on one port at a time. */ if ((port_type != CTL_PORT_NONE) && (targ_port != -1)) { warnx("%s: can only specify one of -t or -n", __func__); retval = 1; goto bailout; } else if ((targ_port == -1) && (port_type == CTL_PORT_NONE)) port_type = CTL_PORT_ALL; bzero(&entry, sizeof(entry)); /* * These are needed for all but list/dump mode. */ entry.port_type = port_type; entry.targ_port = targ_port; switch (port_mode) { case CCTL_PORT_MODE_LIST: cctl_port_dump(fd, quiet, xml, targ_port, port_type); break; case CCTL_PORT_MODE_SET: if (targ_port == -1) { warnx("%s: -w and -W require -n", __func__); retval = 1; goto bailout; } if (wwnn_set) { entry.flags |= CTL_PORT_WWNN_VALID; entry.wwnn = wwnn; } if (wwpn_set) { entry.flags |= CTL_PORT_WWPN_VALID; entry.wwpn = wwpn; } if (ioctl(fd, CTL_SET_PORT_WWNS, &entry) == -1) { warn("%s: CTL_SET_PORT_WWNS ioctl failed", __func__); retval = 1; goto bailout; } break; case CCTL_PORT_MODE_ON: if (ioctl(fd, CTL_ENABLE_PORT, &entry) == -1) { warn("%s: CTL_ENABLE_PORT ioctl failed", __func__); retval = 1; goto bailout; } fprintf(stdout, "Front End Ports enabled\n"); break; case CCTL_PORT_MODE_OFF: if (ioctl(fd, CTL_DISABLE_PORT, &entry) == -1) { warn("%s: CTL_DISABLE_PORT ioctl failed", __func__); retval = 1; goto bailout; } fprintf(stdout, "Front End Ports disabled\n"); break; default: warnx("%s: one of -l, -o or -w/-W must be specified", __func__); retval = 1; goto bailout; break; } bailout: return (retval); bailout_badarg: warnx("%s: only one of -l, -o or -w/-W may be specified", __func__); return (1); } static int cctl_do_io(int fd, int retries, union ctl_io *io, const char *func) { do { if (ioctl(fd, CTL_IO, io) == -1) { warn("%s: error sending CTL_IO ioctl", func); return (-1); } } while (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) && (retries-- > 0)); return (0); } static int cctl_delay(int fd, int target, int lun, int argc, char **argv, char *combinedopt) { struct ctl_io_delay_info delay_info; char *delayloc = NULL; char *delaytype = NULL; int delaytime = -1; int retval; int c; retval = 0; memset(&delay_info, 0, sizeof(delay_info)); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'T': delaytype = strdup(optarg); break; case 'l': delayloc = strdup(optarg); break; case 't': delaytime = strtoul(optarg, NULL, 0); break; } } if (delaytime == -1) { warnx("%s: you must specify the delaytime with -t", __func__); retval = 1; goto bailout; } if (strcasecmp(delayloc, "datamove") == 0) delay_info.delay_loc = CTL_DELAY_LOC_DATAMOVE; else if (strcasecmp(delayloc, "done") == 0) delay_info.delay_loc = CTL_DELAY_LOC_DONE; else { warnx("%s: invalid delay location %s", __func__, delayloc); retval = 1; goto bailout; } if ((delaytype == NULL) || (strcmp(delaytype, "oneshot") == 0)) delay_info.delay_type = CTL_DELAY_TYPE_ONESHOT; else if (strcmp(delaytype, "cont") == 0) delay_info.delay_type = CTL_DELAY_TYPE_CONT; else { warnx("%s: invalid delay type %s", __func__, delaytype); retval = 1; goto bailout; } delay_info.target_id = target; delay_info.lun_id = lun; delay_info.delay_secs = delaytime; if (ioctl(fd, CTL_DELAY_IO, &delay_info) == -1) { warn("%s: CTL_DELAY_IO ioctl failed", __func__); retval = 1; goto bailout; } switch (delay_info.status) { case CTL_DELAY_STATUS_NONE: warnx("%s: no delay status??", __func__); retval = 1; break; case CTL_DELAY_STATUS_OK: break; case CTL_DELAY_STATUS_INVALID_LUN: warnx("%s: invalid lun %d", __func__, lun); retval = 1; break; case CTL_DELAY_STATUS_INVALID_TYPE: warnx("%s: invalid delay type %d", __func__, delay_info.delay_type); retval = 1; break; case CTL_DELAY_STATUS_INVALID_LOC: warnx("%s: delay location %s not implemented?", __func__, delayloc); retval = 1; break; case CTL_DELAY_STATUS_NOT_IMPLEMENTED: warnx("%s: delay not implemented in the kernel", __func__); warnx("%s: recompile with the CTL_IO_DELAY flag set", __func__); retval = 1; break; default: warnx("%s: unknown delay return status %d", __func__, delay_info.status); retval = 1; break; } bailout: /* delayloc should never be NULL, but just in case...*/ if (delayloc != NULL) free(delayloc); return (retval); } static int cctl_realsync(int fd, int argc, char **argv) { int syncstate; int retval; char *syncarg; retval = 0; if (argc != 3) { warnx("%s %s takes exactly one argument", argv[0], argv[1]); retval = 1; goto bailout; } syncarg = argv[2]; if (strncasecmp(syncarg, "query", min(strlen(syncarg), strlen("query"))) == 0) { if (ioctl(fd, CTL_REALSYNC_GET, &syncstate) == -1) { warn("%s: CTL_REALSYNC_GET ioctl failed", __func__); retval = 1; goto bailout; } fprintf(stdout, "SYNCHRONIZE CACHE support is: "); switch (syncstate) { case 0: fprintf(stdout, "OFF\n"); break; case 1: fprintf(stdout, "ON\n"); break; default: fprintf(stdout, "unknown (%d)\n", syncstate); break; } goto bailout; } else if (strcasecmp(syncarg, "on") == 0) { syncstate = 1; } else if (strcasecmp(syncarg, "off") == 0) { syncstate = 0; } else { warnx("%s: invalid realsync argument %s", __func__, syncarg); retval = 1; goto bailout; } if (ioctl(fd, CTL_REALSYNC_SET, &syncstate) == -1) { warn("%s: CTL_REALSYNC_SET ioctl failed", __func__); retval = 1; goto bailout; } bailout: return (retval); } static int cctl_getsetsync(int fd, int target, int lun, ctladm_cmdfunction command, int argc, char **argv, char *combinedopt) { struct ctl_sync_info sync_info; uint32_t ioctl_cmd; int sync_interval = -1; int retval; int c; retval = 0; memset(&sync_info, 0, sizeof(sync_info)); sync_info.target_id = target; sync_info.lun_id = lun; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'i': sync_interval = strtoul(optarg, NULL, 0); break; default: break; } } if (command == CTLADM_CMD_SETSYNC) { if (sync_interval == -1) { warnx("%s: you must specify the sync interval with -i", __func__); retval = 1; goto bailout; } sync_info.sync_interval = sync_interval; ioctl_cmd = CTL_SETSYNC; } else { ioctl_cmd = CTL_GETSYNC; } if (ioctl(fd, ioctl_cmd, &sync_info) == -1) { warn("%s: CTL_%sSYNC ioctl failed", __func__, (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET"); retval = 1; goto bailout; } switch (sync_info.status) { case CTL_GS_SYNC_OK: if (command == CTLADM_CMD_GETSYNC) { fprintf(stdout, "%d:%d: sync interval: %d\n", target, lun, sync_info.sync_interval); } break; case CTL_GS_SYNC_NO_LUN: warnx("%s: unknown target:LUN %d:%d", __func__, target, lun); retval = 1; break; case CTL_GS_SYNC_NONE: default: warnx("%s: unknown CTL_%sSYNC status %d", __func__, (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET", sync_info.status); retval = 1; break; } bailout: return (retval); } static struct ctladm_opts cctl_err_types[] = { {"aborted", CTL_LUN_INJ_ABORTED, CTLADM_ARG_NONE, NULL}, {"mediumerr", CTL_LUN_INJ_MEDIUM_ERR, CTLADM_ARG_NONE, NULL}, {"ua", CTL_LUN_INJ_UA, CTLADM_ARG_NONE, NULL}, {"custom", CTL_LUN_INJ_CUSTOM, CTLADM_ARG_NONE, NULL}, {NULL, 0, 0, NULL} }; static struct ctladm_opts cctl_err_patterns[] = { {"read", CTL_LUN_PAT_READ, CTLADM_ARG_NONE, NULL}, {"write", CTL_LUN_PAT_WRITE, CTLADM_ARG_NONE, NULL}, {"rw", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL}, {"readwrite", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL}, {"readcap", CTL_LUN_PAT_READCAP, CTLADM_ARG_NONE, NULL}, {"tur", CTL_LUN_PAT_TUR, CTLADM_ARG_NONE, NULL}, {"any", CTL_LUN_PAT_ANY, CTLADM_ARG_NONE, NULL}, #if 0 {"cmd", CTL_LUN_PAT_CMD, CTLADM_ARG_NONE, NULL}, #endif {NULL, 0, 0, NULL} }; static int cctl_error_inject(int fd, uint32_t target, uint32_t lun, int argc, char **argv, char *combinedopt) { int retval = 0; struct ctl_error_desc err_desc; uint64_t lba = 0; uint32_t len = 0; uint64_t delete_id = 0; int delete_id_set = 0; int continuous = 0; int sense_len = 0; int fd_sense = 0; int c; bzero(&err_desc, sizeof(err_desc)); err_desc.target_id = target; err_desc.lun_id = lun; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'c': continuous = 1; break; case 'd': delete_id = strtoull(optarg, NULL, 0); delete_id_set = 1; break; case 'i': case 'p': { ctladm_optret optret; ctladm_cmdargs argnum; const char *subopt; if (c == 'i') { ctl_lun_error err_type; if (err_desc.lun_error != CTL_LUN_INJ_NONE) { warnx("%s: can't specify multiple -i " "arguments", __func__); retval = 1; goto bailout; } optret = getoption(cctl_err_types, optarg, &err_type, &argnum, &subopt); err_desc.lun_error = err_type; } else { ctl_lun_error_pattern pattern; optret = getoption(cctl_err_patterns, optarg, &pattern, &argnum, &subopt); err_desc.error_pattern |= pattern; } if (optret == CC_OR_AMBIGUOUS) { warnx("%s: ambiguous argument %s", __func__, optarg); retval = 1; goto bailout; } else if (optret == CC_OR_NOT_FOUND) { warnx("%s: argument %s not found", __func__, optarg); retval = 1; goto bailout; } break; } case 'r': { char *tmpstr, *tmpstr2; tmpstr = strdup(optarg); if (tmpstr == NULL) { warn("%s: error duplicating string %s", __func__, optarg); retval = 1; goto bailout; } tmpstr2 = strsep(&tmpstr, ","); if (tmpstr2 == NULL) { warnx("%s: invalid -r argument %s", __func__, optarg); retval = 1; free(tmpstr); goto bailout; } lba = strtoull(tmpstr2, NULL, 0); tmpstr2 = strsep(&tmpstr, ","); if (tmpstr2 == NULL) { warnx("%s: no len argument for -r lba,len, got" " %s", __func__, optarg); retval = 1; free(tmpstr); goto bailout; } len = strtoul(tmpstr2, NULL, 0); free(tmpstr); break; } case 's': { struct get_hook hook; char *sensestr; sense_len = strtol(optarg, NULL, 0); if (sense_len <= 0) { warnx("invalid number of sense bytes %d", sense_len); retval = 1; goto bailout; } sense_len = MIN(sense_len, SSD_FULL_SIZE); hook.argc = argc - optind; hook.argv = argv + optind; hook.got = 0; sensestr = cget(&hook, NULL); if ((sensestr != NULL) && (sensestr[0] == '-')) { fd_sense = 1; } else { buff_encode_visit( (uint8_t *)&err_desc.custom_sense, sense_len, sensestr, iget, &hook); } optind += hook.got; break; } default: break; } } if (delete_id_set != 0) { err_desc.serial = delete_id; if (ioctl(fd, CTL_ERROR_INJECT_DELETE, &err_desc) == -1) { warn("%s: error issuing CTL_ERROR_INJECT_DELETE ioctl", __func__); retval = 1; } goto bailout; } if (err_desc.lun_error == CTL_LUN_INJ_NONE) { warnx("%s: error injection command (-i) needed", __func__); retval = 1; goto bailout; } else if ((err_desc.lun_error == CTL_LUN_INJ_CUSTOM) && (sense_len == 0)) { warnx("%s: custom error requires -s", __func__); retval = 1; goto bailout; } if (continuous != 0) err_desc.lun_error |= CTL_LUN_INJ_CONTINUOUS; /* * If fd_sense is set, we need to read the sense data the user * wants returned from stdin. */ if (fd_sense == 1) { ssize_t amt_read; int amt_to_read = sense_len; u_int8_t *buf_ptr = (uint8_t *)&err_desc.custom_sense; for (amt_read = 0; amt_to_read > 0; amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) { if (amt_read == -1) { warn("error reading sense data from stdin"); retval = 1; goto bailout; } amt_to_read -= amt_read; buf_ptr += amt_read; } } if (err_desc.error_pattern == CTL_LUN_PAT_NONE) { warnx("%s: command pattern (-p) needed", __func__); retval = 1; goto bailout; } if (len != 0) { err_desc.error_pattern |= CTL_LUN_PAT_RANGE; /* * We could check here to see whether it's a read/write * command, but that will be pointless once we allow * custom patterns. At that point, the user could specify * a READ(6) CDB type, and we wouldn't have an easy way here * to verify whether range checking is possible there. The * user will just figure it out when his error never gets * executed. */ #if 0 if ((err_desc.pattern & CTL_LUN_PAT_READWRITE) == 0) { warnx("%s: need read and/or write pattern if range " "is specified", __func__); retval = 1; goto bailout; } #endif err_desc.lba_range.lba = lba; err_desc.lba_range.len = len; } if (ioctl(fd, CTL_ERROR_INJECT, &err_desc) == -1) { warn("%s: error issuing CTL_ERROR_INJECT ioctl", __func__); retval = 1; } else { printf("Error injection succeeded, serial number is %ju\n", (uintmax_t)err_desc.serial); } bailout: return (retval); } static int cctl_lunlist(int fd) { struct scsi_report_luns_data *lun_data; struct scsi_inquiry_data *inq_data; uint32_t num_luns; int target; int initid; unsigned int i; int retval; retval = 0; inq_data = NULL; target = 6; initid = 7; /* * XXX KDM assuming LUN 0 is fine, but we may need to change this * if we ever acquire the ability to have multiple targets. */ if ((retval = cctl_get_luns(fd, target, /*lun*/ 0, initid, /*retries*/ 2, &lun_data, &num_luns)) != 0) goto bailout; inq_data = malloc(sizeof(*inq_data)); if (inq_data == NULL) { warn("%s: couldn't allocate memory for inquiry data\n", __func__); retval = 1; goto bailout; } for (i = 0; i < num_luns; i++) { char scsi_path[40]; int lun_val; switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) { case RPL_LUNDATA_ATYP_PERIPH: lun_val = lun_data->luns[i].lundata[1]; break; case RPL_LUNDATA_ATYP_FLAT: lun_val = (lun_data->luns[i].lundata[0] & RPL_LUNDATA_FLAT_LUN_MASK) | (lun_data->luns[i].lundata[1] << RPL_LUNDATA_FLAT_LUN_BITS); break; case RPL_LUNDATA_ATYP_LUN: case RPL_LUNDATA_ATYP_EXTLUN: default: fprintf(stdout, "Unsupported LUN format %d\n", lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK); lun_val = -1; break; } if (lun_val == -1) continue; if ((retval = cctl_get_inquiry(fd, target, lun_val, initid, /*retries*/ 2, scsi_path, sizeof(scsi_path), inq_data)) != 0) { goto bailout; } printf("%s", scsi_path); scsi_print_inquiry(inq_data); } bailout: if (lun_data != NULL) free(lun_data); if (inq_data != NULL) free(inq_data); return (retval); } static void cctl_cfi_mt_statusstr(cfi_mt_status status, char *str, int str_len) { switch (status) { case CFI_MT_PORT_OFFLINE: snprintf(str, str_len, "Port Offline"); break; case CFI_MT_ERROR: snprintf(str, str_len, "Error"); break; case CFI_MT_SUCCESS: snprintf(str, str_len, "Success"); break; case CFI_MT_NONE: snprintf(str, str_len, "None??"); break; default: snprintf(str, str_len, "Unknown status: %d", status); break; } } static void cctl_cfi_bbr_statusstr(cfi_bbrread_status status, char *str, int str_len) { switch (status) { case CFI_BBR_SUCCESS: snprintf(str, str_len, "Success"); break; case CFI_BBR_LUN_UNCONFIG: snprintf(str, str_len, "LUN not configured"); break; case CFI_BBR_NO_LUN: snprintf(str, str_len, "LUN does not exist"); break; case CFI_BBR_NO_MEM: snprintf(str, str_len, "Memory allocation error"); break; case CFI_BBR_BAD_LEN: snprintf(str, str_len, "Length is not a multiple of blocksize"); break; case CFI_BBR_RESERV_CONFLICT: snprintf(str, str_len, "Reservation conflict"); break; case CFI_BBR_LUN_STOPPED: snprintf(str, str_len, "LUN is powered off"); break; case CFI_BBR_LUN_OFFLINE_CTL: snprintf(str, str_len, "LUN is offline"); break; case CFI_BBR_LUN_OFFLINE_RC: snprintf(str, str_len, "RAIDCore array is offline (double " "failure?)"); break; case CFI_BBR_SCSI_ERROR: snprintf(str, str_len, "SCSI Error"); break; case CFI_BBR_ERROR: snprintf(str, str_len, "Error"); break; default: snprintf(str, str_len, "Unknown status: %d", status); break; } } static int cctl_hardstopstart(int fd, ctladm_cmdfunction command) { struct ctl_hard_startstop_info hs_info; char error_str[256]; int do_start; int retval; retval = 0; if (command == CTLADM_CMD_HARDSTART) do_start = 1; else do_start = 0; if (ioctl(fd, (do_start == 1) ? CTL_HARD_START : CTL_HARD_STOP, &hs_info) == -1) { warn("%s: CTL_HARD_%s ioctl failed", __func__, (do_start == 1) ? "START" : "STOP"); retval = 1; goto bailout; } fprintf(stdout, "Hard %s Status: ", (command == CTLADM_CMD_HARDSTOP) ? "Stop" : "Start"); cctl_cfi_mt_statusstr(hs_info.status, error_str, sizeof(error_str)); fprintf(stdout, "%s\n", error_str); fprintf(stdout, "Total LUNs: %d\n", hs_info.total_luns); fprintf(stdout, "LUNs complete: %d\n", hs_info.luns_complete); fprintf(stdout, "LUNs failed: %d\n", hs_info.luns_failed); bailout: return (retval); } static int cctl_bbrread(int fd, int target __unused, int lun, int iid __unused, int argc, char **argv, char *combinedopt) { struct ctl_bbrread_info bbr_info; char error_str[256]; int datalen = -1; uint64_t lba = 0; int lba_set = 0; int retval; int c; retval = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'd': datalen = strtoul(optarg, NULL, 0); break; case 'l': lba = strtoull(optarg, NULL, 0); lba_set = 1; break; default: break; } } if (lba_set == 0) { warnx("%s: you must specify an LBA with -l", __func__); retval = 1; goto bailout; } if (datalen == -1) { warnx("%s: you must specify a length with -d", __func__); retval = 1; goto bailout; } bbr_info.lun_num = lun; bbr_info.lba = lba; /* * XXX KDM get the blocksize first?? */ if ((datalen % 512) != 0) { warnx("%s: data length %d is not a multiple of 512 bytes", __func__, datalen); retval = 1; goto bailout; } bbr_info.len = datalen; if (ioctl(fd, CTL_BBRREAD, &bbr_info) == -1) { warn("%s: CTL_BBRREAD ioctl failed", __func__); retval = 1; goto bailout; } cctl_cfi_mt_statusstr(bbr_info.status, error_str, sizeof(error_str)); fprintf(stdout, "BBR Read Overall Status: %s\n", error_str); cctl_cfi_bbr_statusstr(bbr_info.bbr_status, error_str, sizeof(error_str)); fprintf(stdout, "BBR Read Status: %s\n", error_str); /* * XXX KDM should we bother printing out SCSI status if we get * CFI_BBR_SCSI_ERROR back? * * Return non-zero if this fails? */ bailout: return (retval); } static int cctl_startup_shutdown(int fd, int target, int lun, int iid, ctladm_cmdfunction command) { union ctl_io *io; struct ctl_id id; struct scsi_report_luns_data *lun_data; struct scsi_inquiry_data *inq_data; uint32_t num_luns; unsigned int i; int retval; retval = 0; inq_data = NULL; /* * - report luns * - step through each lun, do an inquiry * - check OOA queue on direct access luns * - send stop with offline bit to each direct access device with a * clear OOA queue * - if we get a reservation conflict, reset the LUN to clear it * and reissue the stop with the offline bit set */ id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("%s: can't allocate memory", __func__); return (1); } if ((retval = cctl_get_luns(fd, target, lun, iid, /*retries*/ 2, &lun_data, &num_luns)) != 0) goto bailout; inq_data = malloc(sizeof(*inq_data)); if (inq_data == NULL) { warn("%s: couldn't allocate memory for inquiry data\n", __func__); retval = 1; goto bailout; } for (i = 0; i < num_luns; i++) { char scsi_path[40]; int lun_val; /* * XXX KDM figure out a way to share this code with * cctl_lunlist()? */ switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) { case RPL_LUNDATA_ATYP_PERIPH: lun_val = lun_data->luns[i].lundata[1]; break; case RPL_LUNDATA_ATYP_FLAT: lun_val = (lun_data->luns[i].lundata[0] & RPL_LUNDATA_FLAT_LUN_MASK) | (lun_data->luns[i].lundata[1] << RPL_LUNDATA_FLAT_LUN_BITS); break; case RPL_LUNDATA_ATYP_LUN: case RPL_LUNDATA_ATYP_EXTLUN: default: fprintf(stdout, "Unsupported LUN format %d\n", lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK); lun_val = -1; break; } if (lun_val == -1) continue; if ((retval = cctl_get_inquiry(fd, target, lun_val, iid, /*retries*/ 2, scsi_path, sizeof(scsi_path), inq_data)) != 0) { goto bailout; } printf("%s", scsi_path); scsi_print_inquiry(inq_data); /* * We only want to shutdown direct access devices. */ if (SID_TYPE(inq_data) != T_DIRECT) { printf("%s LUN is not direct access, skipped\n", scsi_path); continue; } if (command == CTLADM_CMD_SHUTDOWN) { struct ctl_ooa_info ooa_info; ooa_info.target_id = target; ooa_info.lun_id = lun_val; if (ioctl(fd, CTL_CHECK_OOA, &ooa_info) == -1) { printf("%s CTL_CHECK_OOA ioctl failed\n", scsi_path); continue; } if (ooa_info.status != CTL_OOA_SUCCESS) { printf("%s CTL_CHECK_OOA returned status %d\n", scsi_path, ooa_info.status); continue; } if (ooa_info.num_entries != 0) { printf("%s %d entr%s in the OOA queue, " "skipping shutdown\n", scsi_path, ooa_info.num_entries, (ooa_info.num_entries > 1)?"ies" : "y" ); continue; } } ctl_scsi_start_stop(/*io*/ io, /*start*/(command == CTLADM_CMD_STARTUP) ? 1 : 0, /*load_eject*/ 0, /*immediate*/ 0, /*power_conditions*/ SSS_PC_START_VALID, /*onoffline*/ 1, /*ctl_tag_type*/ (command == CTLADM_CMD_STARTUP) ? CTL_TAG_SIMPLE :CTL_TAG_ORDERED, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun_val; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, /*retries*/ 3, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) ctl_io_error_print(io, inq_data, stderr); else { printf("%s LUN is now %s\n", scsi_path, (command == CTLADM_CMD_STARTUP) ? "online" : "offline"); } } bailout: if (lun_data != NULL) free(lun_data); if (inq_data != NULL) free(inq_data); if (io != NULL) ctl_scsi_free_io(io); return (retval); } static int cctl_sync_cache(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt) { union ctl_io *io; struct ctl_id id; int cdb_size = -1; int retval; uint64_t our_lba = 0; uint32_t our_block_count = 0; int reladr = 0, immed = 0; int c; id.id = iid; retval = 0; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("%s: can't allocate memory", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'b': our_block_count = strtoul(optarg, NULL, 0); break; case 'c': cdb_size = strtol(optarg, NULL, 0); break; case 'i': immed = 1; break; case 'l': our_lba = strtoull(optarg, NULL, 0); break; case 'r': reladr = 1; break; default: break; } } if (cdb_size != -1) { switch (cdb_size) { case 10: case 16: break; default: warnx("%s: invalid cdbsize %d, valid sizes are 10 " "and 16", __func__, cdb_size); retval = 1; goto bailout; break; /* NOTREACHED */ } } else cdb_size = 10; ctl_scsi_sync_cache(/*io*/ io, /*immed*/ immed, /*reladr*/ reladr, /*minimum_cdb_size*/ cdb_size, /*starting_lba*/ our_lba, /*block_count*/ our_block_count, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { fprintf(stdout, "Cache synchronized successfully\n"); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); return (retval); } static int cctl_start_stop(int fd, int target, int lun, int iid, int retries, int start, int argc, char **argv, char *combinedopt) { union ctl_io *io; struct ctl_id id; char scsi_path[40]; int immed = 0, onoffline = 0; int retval, c; id.id = iid; retval = 0; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("%s: can't allocate memory", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'i': immed = 1; break; case 'o': onoffline = 1; break; default: break; } } /* * Use an ordered tag for the stop command, to guarantee that any * pending I/O will finish before the stop command executes. This * would normally be the case anyway, since CTL will basically * treat the start/stop command as an ordered command with respect * to any other command except an INQUIRY. (See ctl_ser_table.c.) */ ctl_scsi_start_stop(/*io*/ io, /*start*/ start, /*load_eject*/ 0, /*immediate*/ immed, /*power_conditions*/ SSS_PC_START_VALID, /*onoffline*/ onoffline, /*ctl_tag_type*/ start ? CTL_TAG_SIMPLE : CTL_TAG_ORDERED, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path)); if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { fprintf(stdout, "%s LUN %s successfully\n", scsi_path, (start) ? "started" : "stopped"); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); return (retval); } static int cctl_mode_sense(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt) { union ctl_io *io; struct ctl_id id; uint32_t datalen; uint8_t *dataptr; int pc = -1, cdbsize, retval, dbd = 0, subpage = -1; int list = 0; int page_code = -1; int c; id.id = iid; cdbsize = 0; retval = 0; dataptr = NULL; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'P': pc = strtoul(optarg, NULL, 0); break; case 'S': subpage = strtoul(optarg, NULL, 0); break; case 'd': dbd = 1; break; case 'l': list = 1; break; case 'm': page_code = strtoul(optarg, NULL, 0); break; case 'c': cdbsize = strtol(optarg, NULL, 0); break; default: break; } } if (((list == 0) && (page_code == -1)) || ((list != 0) && (page_code != -1))) { warnx("%s: you must specify either a page code (-m) or -l", __func__); retval = 1; goto bailout; } if ((page_code != -1) && ((page_code > SMS_ALL_PAGES_PAGE) || (page_code < 0))) { warnx("%s: page code %d is out of range", __func__, page_code); retval = 1; goto bailout; } if (list == 1) { page_code = SMS_ALL_PAGES_PAGE; if (pc != -1) { warnx("%s: arg -P makes no sense with -l", __func__); retval = 1; goto bailout; } if (subpage != -1) { warnx("%s: arg -S makes no sense with -l", __func__); retval = 1; goto bailout; } } if (pc == -1) pc = SMS_PAGE_CTRL_CURRENT; else { if ((pc > 3) || (pc < 0)) { warnx("%s: page control value %d is out of range: 0-3", __func__, pc); retval = 1; goto bailout; } } if ((subpage != -1) && ((subpage > 255) || (subpage < 0))) { warnx("%s: subpage code %d is out of range: 0-255", __func__, subpage); retval = 1; goto bailout; } if (cdbsize != 0) { switch (cdbsize) { case 6: case 10: break; default: warnx("%s: invalid cdbsize %d, valid sizes are 6 " "and 10", __func__, cdbsize); retval = 1; goto bailout; break; } } else cdbsize = 6; if (subpage == -1) subpage = 0; if (cdbsize == 6) datalen = 255; else datalen = 65535; dataptr = (uint8_t *)malloc(datalen); if (dataptr == NULL) { warn("%s: can't allocate %d bytes", __func__, datalen); retval = 1; goto bailout; } memset(dataptr, 0, datalen); ctl_scsi_mode_sense(io, /*data_ptr*/ dataptr, /*data_len*/ datalen, /*dbd*/ dbd, /*llbaa*/ 0, /*page_code*/ page_code, /*pc*/ pc << 6, /*subpage*/ subpage, /*minimum_cdb_size*/ cdbsize, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { int pages_len, used_len; uint32_t returned_len; uint8_t *ndataptr; if (io->scsiio.cdb[0] == MODE_SENSE_6) { struct scsi_mode_hdr_6 *hdr6; int bdlen; hdr6 = (struct scsi_mode_hdr_6 *)dataptr; returned_len = hdr6->datalen + 1; bdlen = hdr6->block_descr_len; ndataptr = (uint8_t *)((uint8_t *)&hdr6[1] + bdlen); } else { struct scsi_mode_hdr_10 *hdr10; int bdlen; hdr10 = (struct scsi_mode_hdr_10 *)dataptr; returned_len = scsi_2btoul(hdr10->datalen) + 2; bdlen = scsi_2btoul(hdr10->block_descr_len); ndataptr = (uint8_t *)((uint8_t *)&hdr10[1] + bdlen); } /* just in case they can give us more than we allocated for */ returned_len = min(returned_len, datalen); pages_len = returned_len - (ndataptr - dataptr); #if 0 fprintf(stdout, "returned_len = %d, pages_len = %d\n", returned_len, pages_len); #endif if (list == 1) { fprintf(stdout, "Supported mode pages:\n"); for (used_len = 0; used_len < pages_len;) { struct scsi_mode_page_header *header; header = (struct scsi_mode_page_header *) &ndataptr[used_len]; fprintf(stdout, "%d\n", header->page_code); used_len += header->page_length + 2; } } else { for (used_len = 0; used_len < pages_len; used_len++) { fprintf(stdout, "0x%x ", ndataptr[used_len]); if (((used_len+1) % 16) == 0) fprintf(stdout, "\n"); } fprintf(stdout, "\n"); } } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } static int cctl_read_capacity(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt) { union ctl_io *io; struct ctl_id id; struct scsi_read_capacity_data *data; struct scsi_read_capacity_data_long *longdata; int cdbsize = -1, retval; uint8_t *dataptr; int c; cdbsize = 10; dataptr = NULL; retval = 0; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory\n", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'c': cdbsize = strtol(optarg, NULL, 0); break; default: break; } } if (cdbsize != -1) { switch (cdbsize) { case 10: case 16: break; default: warnx("%s: invalid cdbsize %d, valid sizes are 10 " "and 16", __func__, cdbsize); retval = 1; goto bailout; break; /* NOTREACHED */ } } else cdbsize = 10; dataptr = (uint8_t *)malloc(sizeof(*longdata)); if (dataptr == NULL) { warn("%s: can't allocate %zd bytes\n", __func__, sizeof(*longdata)); retval = 1; goto bailout; } memset(dataptr, 0, sizeof(*longdata)); retry: switch (cdbsize) { case 10: ctl_scsi_read_capacity(io, /*data_ptr*/ dataptr, /*data_len*/ sizeof(*longdata), /*addr*/ 0, /*reladr*/ 0, /*pmi*/ 0, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); break; case 16: ctl_scsi_read_capacity_16(io, /*data_ptr*/ dataptr, /*data_len*/ sizeof(*longdata), /*addr*/ 0, /*reladr*/ 0, /*pmi*/ 0, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); break; } io->io_hdr.nexus.initid = id; io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { uint64_t maxlba; uint32_t blocksize; if (cdbsize == 10) { data = (struct scsi_read_capacity_data *)dataptr; maxlba = scsi_4btoul(data->addr); blocksize = scsi_4btoul(data->length); if (maxlba == 0xffffffff) { cdbsize = 16; goto retry; } } else { longdata=(struct scsi_read_capacity_data_long *)dataptr; maxlba = scsi_8btou64(longdata->addr); blocksize = scsi_4btoul(longdata->length); } fprintf(stdout, "Disk Capacity: %ju, Blocksize: %d\n", (uintmax_t)maxlba, blocksize); } else { ctl_io_error_print(io, NULL, stderr); } bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } static int cctl_read_write(int fd, int target, int lun, int iid, int retries, int argc, char **argv, char *combinedopt, ctladm_cmdfunction command) { union ctl_io *io; struct ctl_id id; int file_fd, do_stdio; int cdbsize = -1, databytes; uint8_t *dataptr; char *filename = NULL; int datalen = -1, blocksize = -1; uint64_t lba = 0; int lba_set = 0; int retval; int c; retval = 0; do_stdio = 0; dataptr = NULL; file_fd = -1; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory\n", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'N': io->io_hdr.flags |= CTL_FLAG_NO_DATAMOVE; break; case 'b': blocksize = strtoul(optarg, NULL, 0); break; case 'c': cdbsize = strtoul(optarg, NULL, 0); break; case 'd': datalen = strtoul(optarg, NULL, 0); break; case 'f': filename = strdup(optarg); break; case 'l': lba = strtoull(optarg, NULL, 0); lba_set = 1; break; default: break; } } if (filename == NULL) { warnx("%s: you must supply a filename using -f", __func__); retval = 1; goto bailout; } if (datalen == -1) { warnx("%s: you must specify the data length with -d", __func__); retval = 1; goto bailout; } if (lba_set == 0) { warnx("%s: you must specify the LBA with -l", __func__); retval = 1; goto bailout; } if (blocksize == -1) { warnx("%s: you must specify the blocksize with -b", __func__); retval = 1; goto bailout; } if (cdbsize != -1) { switch (cdbsize) { case 6: case 10: case 12: case 16: break; default: warnx("%s: invalid cdbsize %d, valid sizes are 6, " "10, 12 or 16", __func__, cdbsize); retval = 1; goto bailout; break; /* NOTREACHED */ } } else cdbsize = 6; databytes = datalen * blocksize; dataptr = (uint8_t *)malloc(databytes); if (dataptr == NULL) { warn("%s: can't allocate %d bytes\n", __func__, databytes); retval = 1; goto bailout; } if (strcmp(filename, "-") == 0) { if (command == CTLADM_CMD_READ) file_fd = STDOUT_FILENO; else file_fd = STDIN_FILENO; do_stdio = 1; } else { file_fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (file_fd == -1) { warn("%s: can't open file %s", __func__, filename); retval = 1; goto bailout; } } memset(dataptr, 0, databytes); if (command == CTLADM_CMD_WRITE) { int bytes_read; bytes_read = read(file_fd, dataptr, databytes); if (bytes_read == -1) { warn("%s: error reading file %s", __func__, filename); retval = 1; goto bailout; } if (bytes_read != databytes) { warnx("%s: only read %d bytes from file %s", __func__, bytes_read, filename); retval = 1; goto bailout; } } ctl_scsi_read_write(io, /*data_ptr*/ dataptr, /*data_len*/ databytes, /*read_op*/ (command == CTLADM_CMD_READ) ? 1 : 0, /*byte2*/ 0, /*minimum_cdb_size*/ cdbsize, /*lba*/ lba, /*num_blocks*/ datalen, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if (((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) && (command == CTLADM_CMD_READ)) { int bytes_written; bytes_written = write(file_fd, dataptr, databytes); if (bytes_written == -1) { warn("%s: can't write to %s", __func__, filename); goto bailout; } } else if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); if ((do_stdio == 0) && (file_fd != -1)) close(file_fd); return (retval); } static int cctl_get_luns(int fd, int target, int lun, int iid, int retries, struct scsi_report_luns_data **lun_data, uint32_t *num_luns) { union ctl_io *io; struct ctl_id id; uint32_t nluns; int lun_datalen; int retval; retval = 0; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("%s: can't allocate memory", __func__); return (1); } /* * lun_data includes space for 1 lun, allocate space for 4 initially. * If that isn't enough, we'll allocate more. */ nluns = 4; retry: lun_datalen = sizeof(*lun_data) + (nluns * sizeof(struct scsi_report_luns_lundata)); *lun_data = malloc(lun_datalen); if (*lun_data == NULL) { warnx("%s: can't allocate memory", __func__); ctl_scsi_free_io(io); return (1); } ctl_scsi_report_luns(io, /*data_ptr*/ (uint8_t *)*lun_data, /*data_len*/ lun_datalen, /*select_report*/ RPL_REPORT_ALL, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.initid = id; io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { uint32_t returned_len, returned_luns; returned_len = scsi_4btoul((*lun_data)->length); returned_luns = returned_len / 8; if (returned_luns > nluns) { nluns = returned_luns; free(*lun_data); goto retry; } /* These should be the same */ *num_luns = MIN(returned_luns, nluns); } else { ctl_io_error_print(io, NULL, stderr); retval = 1; } bailout: ctl_scsi_free_io(io); return (retval); } static int cctl_report_luns(int fd, int target, int lun, int iid, int retries) { struct scsi_report_luns_data *lun_data; uint32_t num_luns, i; int retval; lun_data = NULL; if ((retval = cctl_get_luns(fd, target, lun, iid, retries, &lun_data, &num_luns)) != 0) goto bailout; fprintf(stdout, "%u LUNs returned\n", num_luns); for (i = 0; i < num_luns; i++) { int lun_val; /* * XXX KDM figure out a way to share this code with * cctl_lunlist()? */ switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) { case RPL_LUNDATA_ATYP_PERIPH: lun_val = lun_data->luns[i].lundata[1]; break; case RPL_LUNDATA_ATYP_FLAT: lun_val = (lun_data->luns[i].lundata[0] & RPL_LUNDATA_FLAT_LUN_MASK) | (lun_data->luns[i].lundata[1] << RPL_LUNDATA_FLAT_LUN_BITS); break; case RPL_LUNDATA_ATYP_LUN: case RPL_LUNDATA_ATYP_EXTLUN: default: fprintf(stdout, "Unsupported LUN format %d\n", lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK); lun_val = -1; break; } if (lun_val == -1) continue; fprintf(stdout, "%d\n", lun_val); } bailout: if (lun_data != NULL) free(lun_data); return (retval); } static int cctl_tur(int fd, int target, int lun, int iid, int retries) { union ctl_io *io; struct ctl_id id; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { fprintf(stderr, "can't allocate memory\n"); return (1); } ctl_scsi_tur(io, /* tag_type */ CTL_TAG_SIMPLE, /* control */ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { ctl_scsi_free_io(io); return (1); } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) fprintf(stdout, "Unit is ready\n"); else ctl_io_error_print(io, NULL, stderr); return (0); } static int cctl_get_inquiry(int fd, int target, int lun, int iid, int retries, char *path_str, int path_len, struct scsi_inquiry_data *inq_data) { union ctl_io *io; struct ctl_id id; int retval; retval = 0; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("cctl_inquiry: can't allocate memory\n"); return (1); } ctl_scsi_inquiry(/*io*/ io, /*data_ptr*/ (uint8_t *)inq_data, /*data_len*/ sizeof(*inq_data), /*byte2*/ 0, /*page_code*/ 0, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) { retval = 1; ctl_io_error_print(io, NULL, stderr); } else if (path_str != NULL) ctl_scsi_path_string(io, path_str, path_len); bailout: ctl_scsi_free_io(io); return (retval); } static int cctl_inquiry(int fd, int target, int lun, int iid, int retries) { struct scsi_inquiry_data *inq_data; char scsi_path[40]; int retval; retval = 0; inq_data = malloc(sizeof(*inq_data)); if (inq_data == NULL) { warnx("%s: can't allocate inquiry data", __func__); retval = 1; goto bailout; } if ((retval = cctl_get_inquiry(fd, target, lun, iid, retries, scsi_path, sizeof(scsi_path), inq_data)) != 0) goto bailout; printf("%s", scsi_path); scsi_print_inquiry(inq_data); bailout: if (inq_data != NULL) free(inq_data); return (retval); } static int cctl_req_sense(int fd, int target, int lun, int iid, int retries) { union ctl_io *io; struct scsi_sense_data *sense_data; struct ctl_id id; int retval; retval = 0; id.id = iid; io = ctl_scsi_alloc_io(id); if (io == NULL) { warnx("cctl_req_sense: can't allocate memory\n"); return (1); } sense_data = malloc(sizeof(*sense_data)); memset(sense_data, 0, sizeof(*sense_data)); ctl_scsi_request_sense(/*io*/ io, /*data_ptr*/ (uint8_t *)sense_data, /*data_len*/ sizeof(*sense_data), /*byte2*/ 0, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retries, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { bcopy(sense_data, &io->scsiio.sense_data, sizeof(*sense_data)); io->scsiio.sense_len = sizeof(*sense_data); ctl_scsi_sense_print(&io->scsiio, NULL, stdout); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); free(sense_data); return (retval); } static int cctl_report_target_port_group(int fd, int target, int lun, int initiator) { union ctl_io *io; struct ctl_id id; uint32_t datalen; uint8_t *dataptr; int retval; id.id = initiator; dataptr = NULL; retval = 0; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory", __func__); return (1); } datalen = 64; dataptr = (uint8_t *)malloc(datalen); if (dataptr == NULL) { warn("%s: can't allocate %d bytes", __func__, datalen); retval = 1; goto bailout; } memset(dataptr, 0, datalen); ctl_scsi_maintenance_in(/*io*/ io, /*data_ptr*/ dataptr, /*data_len*/ datalen, /*action*/ SA_RPRT_TRGT_GRP, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, 0, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { int returned_len, used_len; returned_len = scsi_4btoul(&dataptr[0]) + 4; for (used_len = 0; used_len < returned_len; used_len++) { fprintf(stdout, "0x%02x ", dataptr[used_len]); if (((used_len+1) % 8) == 0) fprintf(stdout, "\n"); } fprintf(stdout, "\n"); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } static int cctl_inquiry_vpd_devid(int fd, int target, int lun, int initiator) { union ctl_io *io; struct ctl_id id; uint32_t datalen; uint8_t *dataptr; int retval; id.id = initiator; retval = 0; dataptr = NULL; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory", __func__); return (1); } datalen = 256; dataptr = (uint8_t *)malloc(datalen); if (dataptr == NULL) { warn("%s: can't allocate %d bytes", __func__, datalen); retval = 1; goto bailout; } memset(dataptr, 0, datalen); ctl_scsi_inquiry(/*io*/ io, /*data_ptr*/ dataptr, /*data_len*/ datalen, /*byte2*/ SI_EVPD, /*page_code*/ SVPD_DEVICE_ID, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, 0, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { int returned_len, used_len; returned_len = scsi_2btoul(&dataptr[2]) + 4; for (used_len = 0; used_len < returned_len; used_len++) { fprintf(stdout, "0x%02x ", dataptr[used_len]); if (((used_len+1) % 8) == 0) fprintf(stdout, "\n"); } fprintf(stdout, "\n"); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } static int cctl_persistent_reserve_in(int fd, int target, int lun, int initiator, int argc, char **argv, char *combinedopt, int retry_count) { union ctl_io *io; struct ctl_id id; uint32_t datalen; uint8_t *dataptr; int action = -1; int retval; int c; id.id = initiator; retval = 0; dataptr = NULL; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': action = strtol(optarg, NULL, 0); break; default: break; } } if (action < 0 || action > 2) { warn("action must be specified and in the range: 0-2"); retval = 1; goto bailout; } datalen = 256; dataptr = (uint8_t *)malloc(datalen); if (dataptr == NULL) { warn("%s: can't allocate %d bytes", __func__, datalen); retval = 1; goto bailout; } memset(dataptr, 0, datalen); ctl_scsi_persistent_res_in(io, /*data_ptr*/ dataptr, /*data_len*/ datalen, /*action*/ action, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retry_count, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { int returned_len, used_len; returned_len = 0; switch (action) { case 0: returned_len = scsi_4btoul(&dataptr[4]) + 8; returned_len = min(returned_len, 256); break; case 1: returned_len = scsi_4btoul(&dataptr[4]) + 8; break; case 2: returned_len = 8; break; default: warnx("%s: invalid action %d", __func__, action); goto bailout; break; /* NOTREACHED */ } for (used_len = 0; used_len < returned_len; used_len++) { fprintf(stdout, "0x%02x ", dataptr[used_len]); if (((used_len+1) % 8) == 0) fprintf(stdout, "\n"); } fprintf(stdout, "\n"); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } static int cctl_persistent_reserve_out(int fd, int target, int lun, int initiator, int argc, char **argv, char *combinedopt, int retry_count) { union ctl_io *io; struct ctl_id id; uint32_t datalen; uint64_t key = 0, sa_key = 0; int action = -1, restype = -1; uint8_t *dataptr; int retval; int c; id.id = initiator; retval = 0; dataptr = NULL; io = ctl_scsi_alloc_io(id); if (io == NULL) { warn("%s: can't allocate memory", __func__); return (1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': action = strtol(optarg, NULL, 0); break; case 'k': key = strtoull(optarg, NULL, 0); break; case 'r': restype = strtol(optarg, NULL, 0); break; case 's': sa_key = strtoull(optarg, NULL, 0); break; default: break; } } if (action < 0 || action > 5) { warn("action must be specified and in the range: 0-5"); retval = 1; goto bailout; } if (restype < 0 || restype > 5) { if (action != 0 && action != 5 && action != 3) { warn("'restype' must specified and in the range: 0-5"); retval = 1; goto bailout; } } datalen = 24; dataptr = (uint8_t *)malloc(datalen); if (dataptr == NULL) { warn("%s: can't allocate %d bytes", __func__, datalen); retval = 1; goto bailout; } memset(dataptr, 0, datalen); ctl_scsi_persistent_res_out(io, /*data_ptr*/ dataptr, /*data_len*/ datalen, /*action*/ action, /*type*/ restype, /*key*/ key, /*sa key*/ sa_key, /*tag_type*/ CTL_TAG_SIMPLE, /*control*/ 0); io->io_hdr.nexus.targ_target.id = target; io->io_hdr.nexus.targ_lun = lun; io->io_hdr.nexus.initid = id; if (cctl_do_io(fd, retry_count, io, __func__) != 0) { retval = 1; goto bailout; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) { char scsi_path[40]; ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path)); fprintf( stdout, "%sPERSISTENT RESERVE OUT executed " "successfully\n", scsi_path); } else ctl_io_error_print(io, NULL, stderr); bailout: ctl_scsi_free_io(io); if (dataptr != NULL) free(dataptr); return (retval); } struct cctl_req_option { char *name; int namelen; char *value; int vallen; STAILQ_ENTRY(cctl_req_option) links; }; static int cctl_create_lun(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_req req; int device_type = -1; uint64_t lun_size = 0; uint32_t blocksize = 0, req_lun_id = 0; char *serial_num = NULL; char *device_id = NULL; int lun_size_set = 0, blocksize_set = 0, lun_id_set = 0; char *backend_name = NULL; STAILQ_HEAD(, cctl_req_option) option_list; int num_options = 0; int retval = 0, c; STAILQ_INIT(&option_list); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'b': backend_name = strdup(optarg); break; case 'B': blocksize = strtoul(optarg, NULL, 0); blocksize_set = 1; break; case 'd': device_id = strdup(optarg); break; case 'l': req_lun_id = strtoul(optarg, NULL, 0); lun_id_set = 1; break; case 'o': { struct cctl_req_option *option; char *tmpstr; char *name, *value; tmpstr = strdup(optarg); name = strsep(&tmpstr, "="); if (name == NULL) { warnx("%s: option -o takes \"name=value\"" "argument", __func__); retval = 1; goto bailout; } value = strsep(&tmpstr, "="); if (value == NULL) { warnx("%s: option -o takes \"name=value\"" "argument", __func__); retval = 1; goto bailout; } option = malloc(sizeof(*option)); if (option == NULL) { warn("%s: error allocating %zd bytes", __func__, sizeof(*option)); retval = 1; goto bailout; } option->name = strdup(name); option->namelen = strlen(name) + 1; option->value = strdup(value); option->vallen = strlen(value) + 1; free(tmpstr); STAILQ_INSERT_TAIL(&option_list, option, links); num_options++; break; } case 's': if (strcasecmp(optarg, "auto") != 0) { retval = expand_number(optarg, &lun_size); if (retval != 0) { warn("%s: invalid -s argument", __func__); retval = 1; goto bailout; } } lun_size_set = 1; break; case 'S': serial_num = strdup(optarg); break; case 't': device_type = strtoul(optarg, NULL, 0); break; default: break; } } if (backend_name == NULL) { warnx("%s: backend name (-b) must be specified", __func__); retval = 1; goto bailout; } bzero(&req, sizeof(req)); strlcpy(req.backend, backend_name, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_CREATE; if (blocksize_set != 0) req.reqdata.create.blocksize_bytes = blocksize; if (lun_size_set != 0) req.reqdata.create.lun_size_bytes = lun_size; if (lun_id_set != 0) { req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ; req.reqdata.create.req_lun_id = req_lun_id; } req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; if (device_type != -1) req.reqdata.create.device_type = device_type; else req.reqdata.create.device_type = T_DIRECT; if (serial_num != NULL) { strlcpy(req.reqdata.create.serial_num, serial_num, sizeof(req.reqdata.create.serial_num)); req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM; } if (device_id != NULL) { strlcpy(req.reqdata.create.device_id, device_id, sizeof(req.reqdata.create.device_id)); req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID; } req.num_be_args = num_options; if (num_options > 0) { struct cctl_req_option *option, *next_option; int i; req.be_args = malloc(num_options * sizeof(*req.be_args)); if (req.be_args == NULL) { warn("%s: error allocating %zd bytes", __func__, num_options * sizeof(*req.be_args)); retval = 1; goto bailout; } for (i = 0, option = STAILQ_FIRST(&option_list); i < num_options; i++, option = next_option) { next_option = STAILQ_NEXT(option, links); req.be_args[i].namelen = option->namelen; req.be_args[i].name = strdup(option->name); req.be_args[i].vallen = option->vallen; req.be_args[i].value = strdup(option->value); /* * XXX KDM do we want a way to specify a writeable * flag of some sort? Do we want a way to specify * binary data? */ req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD; STAILQ_REMOVE(&option_list, option, cctl_req_option, links); free(option->name); free(option->value); free(option); } } if (ioctl(fd, CTL_LUN_REQ, &req) == -1) { warn("%s: error issuing CTL_LUN_REQ ioctl", __func__); retval = 1; goto bailout; } switch (req.status) { case CTL_LUN_ERROR: warnx("LUN creation error: %s", req.error_str); retval = 1; goto bailout; case CTL_LUN_WARNING: warnx("LUN creation warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: warnx("unknown LUN creation status: %d", req.status); retval = 1; goto bailout; } fprintf(stdout, "LUN created successfully\n"); fprintf(stdout, "backend: %s\n", req.backend); fprintf(stdout, "device type: %d\n",req.reqdata.create.device_type); fprintf(stdout, "LUN size: %ju bytes\n", (uintmax_t)req.reqdata.create.lun_size_bytes); fprintf(stdout, "blocksize %u bytes\n", req.reqdata.create.blocksize_bytes); fprintf(stdout, "LUN ID: %d\n", req.reqdata.create.req_lun_id); fprintf(stdout, "Serial Number: %s\n", req.reqdata.create.serial_num); fprintf(stdout, "Device ID; %s\n", req.reqdata.create.device_id); bailout: return (retval); } static int cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_req req; uint32_t lun_id = 0; int lun_id_set = 0; char *backend_name = NULL; STAILQ_HEAD(, cctl_req_option) option_list; int num_options = 0; int retval = 0, c; STAILQ_INIT(&option_list); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'b': backend_name = strdup(optarg); break; case 'l': lun_id = strtoul(optarg, NULL, 0); lun_id_set = 1; break; case 'o': { struct cctl_req_option *option; char *tmpstr; char *name, *value; tmpstr = strdup(optarg); name = strsep(&tmpstr, "="); if (name == NULL) { warnx("%s: option -o takes \"name=value\"" "argument", __func__); retval = 1; goto bailout; } value = strsep(&tmpstr, "="); if (value == NULL) { warnx("%s: option -o takes \"name=value\"" "argument", __func__); retval = 1; goto bailout; } option = malloc(sizeof(*option)); if (option == NULL) { warn("%s: error allocating %zd bytes", __func__, sizeof(*option)); retval = 1; goto bailout; } option->name = strdup(name); option->namelen = strlen(name) + 1; option->value = strdup(value); option->vallen = strlen(value) + 1; free(tmpstr); STAILQ_INSERT_TAIL(&option_list, option, links); num_options++; break; } default: break; } } if (backend_name == NULL) errx(1, "%s: backend name (-b) must be specified", __func__); if (lun_id_set == 0) errx(1, "%s: LUN id (-l) must be specified", __func__); bzero(&req, sizeof(req)); strlcpy(req.backend, backend_name, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_RM; req.reqdata.rm.lun_id = lun_id; req.num_be_args = num_options; if (num_options > 0) { struct cctl_req_option *option, *next_option; int i; req.be_args = malloc(num_options * sizeof(*req.be_args)); if (req.be_args == NULL) { warn("%s: error allocating %zd bytes", __func__, num_options * sizeof(*req.be_args)); retval = 1; goto bailout; } for (i = 0, option = STAILQ_FIRST(&option_list); i < num_options; i++, option = next_option) { next_option = STAILQ_NEXT(option, links); req.be_args[i].namelen = option->namelen; req.be_args[i].name = strdup(option->name); req.be_args[i].vallen = option->vallen; req.be_args[i].value = strdup(option->value); /* * XXX KDM do we want a way to specify a writeable * flag of some sort? Do we want a way to specify * binary data? */ req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD; STAILQ_REMOVE(&option_list, option, cctl_req_option, links); free(option->name); free(option->value); free(option); } } if (ioctl(fd, CTL_LUN_REQ, &req) == -1) { warn("%s: error issuing CTL_LUN_REQ ioctl", __func__); retval = 1; goto bailout; } switch (req.status) { case CTL_LUN_ERROR: warnx("LUN removal error: %s", req.error_str); retval = 1; goto bailout; case CTL_LUN_WARNING: warnx("LUN removal warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: warnx("unknown LUN removal status: %d", req.status); retval = 1; goto bailout; } printf("LUN %d removed successfully\n", lun_id); bailout: return (retval); } static int cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_req req; uint64_t lun_size = 0; uint32_t lun_id = 0; int lun_id_set = 0, lun_size_set = 0; char *backend_name = NULL; int retval = 0, c; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'b': backend_name = strdup(optarg); break; case 'l': lun_id = strtoul(optarg, NULL, 0); lun_id_set = 1; break; case 's': if (strcasecmp(optarg, "auto") != 0) { retval = expand_number(optarg, &lun_size); if (retval != 0) { warn("%s: invalid -s argument", __func__); retval = 1; goto bailout; } } lun_size_set = 1; break; default: break; } } if (backend_name == NULL) errx(1, "%s: backend name (-b) must be specified", __func__); if (lun_id_set == 0) errx(1, "%s: LUN id (-l) must be specified", __func__); if (lun_size_set == 0) errx(1, "%s: size (-s) must be specified", __func__); bzero(&req, sizeof(req)); strlcpy(req.backend, backend_name, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_MODIFY; req.reqdata.modify.lun_id = lun_id; req.reqdata.modify.lun_size_bytes = lun_size; if (ioctl(fd, CTL_LUN_REQ, &req) == -1) { warn("%s: error issuing CTL_LUN_REQ ioctl", __func__); retval = 1; goto bailout; } switch (req.status) { case CTL_LUN_ERROR: warnx("LUN modification error: %s", req.error_str); retval = 1; goto bailout; case CTL_LUN_WARNING: warnx("LUN modification warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: warnx("unknown LUN modification status: %d", req.status); retval = 1; goto bailout; } printf("LUN %d modified successfully\n", lun_id); bailout: return (retval); } struct cctl_islist_conn { int connection_id; char *initiator; char *initiator_addr; char *initiator_alias; char *target; char *target_alias; char *header_digest; char *data_digest; char *max_data_segment_length;; int immediate_data; int iser; STAILQ_ENTRY(cctl_islist_conn) links; }; struct cctl_islist_data { int num_conns; STAILQ_HEAD(,cctl_islist_conn) conn_list; struct cctl_islist_conn *cur_conn; int level; struct sbuf *cur_sb[32]; }; static void cctl_islist_start_element(void *user_data, const char *name, const char **attr) { int i; struct cctl_islist_data *islist; struct cctl_islist_conn *cur_conn; islist = (struct cctl_islist_data *)user_data; cur_conn = islist->cur_conn; islist->level++; if ((u_int)islist->level >= (sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0])); islist->cur_sb[islist->level] = sbuf_new_auto(); if (islist->cur_sb[islist->level] == NULL) err(1, "%s: Unable to allocate sbuf", __func__); if (strcmp(name, "connection") == 0) { if (cur_conn != NULL) errx(1, "%s: improper connection element nesting", __func__); cur_conn = calloc(1, sizeof(*cur_conn)); if (cur_conn == NULL) err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_conn)); islist->num_conns++; islist->cur_conn = cur_conn; STAILQ_INSERT_TAIL(&islist->conn_list, cur_conn, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_conn->connection_id = strtoull(attr[i+1], NULL, 0); } else { errx(1, "%s: invalid connection attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_islist_end_element(void *user_data, const char *name) { struct cctl_islist_data *islist; struct cctl_islist_conn *cur_conn; char *str; islist = (struct cctl_islist_data *)user_data; cur_conn = islist->cur_conn; if ((cur_conn == NULL) && (strcmp(name, "ctlislist") != 0)) errx(1, "%s: cur_conn == NULL! (name = %s)", __func__, name); if (islist->cur_sb[islist->level] == NULL) errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, islist->level, name); sbuf_finish(islist->cur_sb[islist->level]); str = strdup(sbuf_data(islist->cur_sb[islist->level])); if (str == NULL) err(1, "%s can't allocate %zd bytes for string", __func__, sbuf_len(islist->cur_sb[islist->level])); sbuf_delete(islist->cur_sb[islist->level]); islist->cur_sb[islist->level] = NULL; islist->level--; if (strcmp(name, "initiator") == 0) { cur_conn->initiator = str; str = NULL; } else if (strcmp(name, "initiator_addr") == 0) { cur_conn->initiator_addr = str; str = NULL; } else if (strcmp(name, "initiator_alias") == 0) { cur_conn->initiator_alias = str; str = NULL; } else if (strcmp(name, "target") == 0) { cur_conn->target = str; str = NULL; } else if (strcmp(name, "target_alias") == 0) { cur_conn->target_alias = str; str = NULL; + } else if (strcmp(name, "target_portal_group_tag") == 0) { } else if (strcmp(name, "header_digest") == 0) { cur_conn->header_digest = str; str = NULL; } else if (strcmp(name, "data_digest") == 0) { cur_conn->data_digest = str; str = NULL; } else if (strcmp(name, "max_data_segment_length") == 0) { cur_conn->max_data_segment_length = str; str = NULL; } else if (strcmp(name, "immediate_data") == 0) { cur_conn->immediate_data = atoi(str); } else if (strcmp(name, "iser") == 0) { cur_conn->iser = atoi(str); } else if (strcmp(name, "connection") == 0) { islist->cur_conn = NULL; } else if (strcmp(name, "ctlislist") == 0) { } else errx(1, "unknown element %s", name); free(str); } static void cctl_islist_char_handler(void *user_data, const XML_Char *str, int len) { struct cctl_islist_data *islist; islist = (struct cctl_islist_data *)user_data; sbuf_bcat(islist->cur_sb[islist->level], str, len); } static int cctl_islist(int fd, int argc, char **argv, char *combinedopt) { struct ctl_iscsi req; struct cctl_islist_data islist; struct cctl_islist_conn *conn; XML_Parser parser; char *conn_str; int conn_len; int dump_xml = 0; int c, retval, verbose = 0; retval = 0; conn_len = 4096; bzero(&islist, sizeof(islist)); STAILQ_INIT(&islist.conn_list); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'v': verbose = 1; break; case 'x': dump_xml = 1; break; default: break; } } retry: conn_str = malloc(conn_len); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LIST; req.data.list.alloc_len = conn_len; req.data.list.conn_xml = conn_str; if (ioctl(fd, CTL_ISCSI, &req) == -1) { warn("%s: error issuing CTL_ISCSI ioctl", __func__); retval = 1; goto bailout; } if (req.status == CTL_ISCSI_ERROR) { warnx("%s: error returned from CTL_ISCSI ioctl:\n%s", __func__, req.error_str); } else if (req.status == CTL_ISCSI_LIST_NEED_MORE_SPACE) { conn_len = conn_len << 1; goto retry; } if (dump_xml != 0) { printf("%s", conn_str); goto bailout; } parser = XML_ParserCreate(NULL); if (parser == NULL) { warn("%s: Unable to create XML parser", __func__); retval = 1; goto bailout; } XML_SetUserData(parser, &islist); XML_SetElementHandler(parser, cctl_islist_start_element, cctl_islist_end_element); XML_SetCharacterDataHandler(parser, cctl_islist_char_handler); retval = XML_Parse(parser, conn_str, strlen(conn_str), 1); if (retval != 1) { warnx("%s: Unable to parse XML: Error %d", __func__, XML_GetErrorCode(parser)); XML_ParserFree(parser); retval = 1; goto bailout; } XML_ParserFree(parser); if (verbose != 0) { STAILQ_FOREACH(conn, &islist.conn_list, links) { printf("Session ID: %d\n", conn->connection_id); printf("Initiator name: %s\n", conn->initiator); printf("Initiator portal: %s\n", conn->initiator_addr); printf("Initiator alias: %s\n", conn->initiator_alias); printf("Target name: %s\n", conn->target); printf("Target alias: %s\n", conn->target_alias); printf("Header digest: %s\n", conn->header_digest); printf("Data digest: %s\n", conn->data_digest); printf("DataSegmentLen: %s\n", conn->max_data_segment_length); printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No"); printf("iSER (RDMA): %s\n", conn->iser ? "Yes" : "No"); printf("\n"); } } else { printf("%4s %-16s %-36s %-36s\n", "ID", "Portal", "Initiator name", "Target name"); STAILQ_FOREACH(conn, &islist.conn_list, links) { printf("%4u %-16s %-36s %-36s\n", conn->connection_id, conn->initiator_addr, conn->initiator, conn->target); } } bailout: free(conn_str); return (retval); } static int cctl_islogout(int fd, int argc, char **argv, char *combinedopt) { struct ctl_iscsi req; int retval = 0, c; int all = 0, connection_id = -1, nargs = 0; char *initiator_name = NULL, *initiator_addr = NULL; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': all = 1; nargs++; break; case 'c': connection_id = strtoul(optarg, NULL, 0); nargs++; break; case 'i': initiator_name = strdup(optarg); if (initiator_name == NULL) err(1, "%s: strdup", __func__); nargs++; break; case 'p': initiator_addr = strdup(optarg); if (initiator_addr == NULL) err(1, "%s: strdup", __func__); nargs++; break; default: break; } } if (nargs == 0) errx(1, "%s: either -a, -c, -i, or -p must be specified", __func__); if (nargs > 1) errx(1, "%s: only one of -a, -c, -i, or -p may be specified", __func__); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LOGOUT; req.data.logout.connection_id = connection_id; if (initiator_addr != NULL) strlcpy(req.data.logout.initiator_addr, initiator_addr, sizeof(req.data.logout.initiator_addr)); if (initiator_name != NULL) strlcpy(req.data.logout.initiator_name, initiator_name, sizeof(req.data.logout.initiator_name)); if (all != 0) req.data.logout.all = 1; if (ioctl(fd, CTL_ISCSI, &req) == -1) { warn("%s: error issuing CTL_ISCSI ioctl", __func__); retval = 1; goto bailout; } if (req.status != CTL_ISCSI_OK) { warnx("%s: error returned from CTL iSCSI logout request:\n%s", __func__, req.error_str); retval = 1; goto bailout; } printf("iSCSI logout requests submitted\n"); bailout: return (retval); } static int cctl_isterminate(int fd, int argc, char **argv, char *combinedopt) { struct ctl_iscsi req; int retval = 0, c; int all = 0, connection_id = -1, nargs = 0; char *initiator_name = NULL, *initiator_addr = NULL; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': all = 1; nargs++; break; case 'c': connection_id = strtoul(optarg, NULL, 0); nargs++; break; case 'i': initiator_name = strdup(optarg); if (initiator_name == NULL) err(1, "%s: strdup", __func__); nargs++; break; case 'p': initiator_addr = strdup(optarg); if (initiator_addr == NULL) err(1, "%s: strdup", __func__); nargs++; break; default: break; } } if (nargs == 0) errx(1, "%s: either -a, -c, -i, or -p must be specified", __func__); if (nargs > 1) errx(1, "%s: only one of -a, -c, -i, or -p may be specified", __func__); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_TERMINATE; req.data.terminate.connection_id = connection_id; if (initiator_addr != NULL) strlcpy(req.data.terminate.initiator_addr, initiator_addr, sizeof(req.data.terminate.initiator_addr)); if (initiator_name != NULL) strlcpy(req.data.terminate.initiator_name, initiator_name, sizeof(req.data.terminate.initiator_name)); if (all != 0) req.data.terminate.all = 1; if (ioctl(fd, CTL_ISCSI, &req) == -1) { warn("%s: error issuing CTL_ISCSI ioctl", __func__); retval = 1; goto bailout; } if (req.status != CTL_ISCSI_OK) { warnx("%s: error returned from CTL iSCSI connection " "termination request:\n%s", __func__, req.error_str); retval = 1; goto bailout; } printf("iSCSI connections terminated\n"); bailout: return (retval); } /* * Name/value pair used for per-LUN attributes. */ struct cctl_lun_nv { char *name; char *value; STAILQ_ENTRY(cctl_lun_nv) links; }; /* * Backend LUN information. */ struct cctl_lun { uint64_t lun_id; char *backend_type; uint64_t size_blocks; uint32_t blocksize; char *serial_number; char *device_id; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_lun) links; }; struct cctl_devlist_data { int num_luns; STAILQ_HEAD(,cctl_lun) lun_list; struct cctl_lun *cur_lun; int level; struct sbuf *cur_sb[32]; }; static void cctl_start_element(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); devlist->cur_sb[devlist->level] = sbuf_new_auto(); if (devlist->cur_sb[devlist->level] == NULL) err(1, "%s: Unable to allocate sbuf", __func__); if (strcmp(name, "lun") == 0) { if (cur_lun != NULL) errx(1, "%s: improper lun element nesting", __func__); cur_lun = calloc(1, sizeof(*cur_lun)); if (cur_lun == NULL) err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_lun)); devlist->num_luns++; devlist->cur_lun = cur_lun; STAILQ_INIT(&cur_lun->attr_list); STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_lun->lun_id = strtoull(attr[i+1], NULL, 0); } else { errx(1, "%s: invalid LUN attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_end_element(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; if ((cur_lun == NULL) && (strcmp(name, "ctllunlist") != 0)) errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name); if (devlist->cur_sb[devlist->level] == NULL) errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, devlist->level, name); if (sbuf_finish(devlist->cur_sb[devlist->level]) != 0) err(1, "%s: sbuf_finish", __func__); str = strdup(sbuf_data(devlist->cur_sb[devlist->level])); if (str == NULL) err(1, "%s can't allocate %zd bytes for string", __func__, sbuf_len(devlist->cur_sb[devlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(devlist->cur_sb[devlist->level]); devlist->cur_sb[devlist->level] = NULL; devlist->level--; if (strcmp(name, "backend_type") == 0) { cur_lun->backend_type = str; str = NULL; } else if (strcmp(name, "size") == 0) { cur_lun->size_blocks = strtoull(str, NULL, 0); } else if (strcmp(name, "blocksize") == 0) { cur_lun->blocksize = strtoul(str, NULL, 0); } else if (strcmp(name, "serial_number") == 0) { cur_lun->serial_number = str; str = NULL; } else if (strcmp(name, "device_id") == 0) { cur_lun->device_id = str; str = NULL; } else if (strcmp(name, "lun") == 0) { devlist->cur_lun = NULL; } else if (strcmp(name, "ctllunlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); nv->name = strdup(name); if (nv->name == NULL) err(1, "%s: can't allocated %zd bytes for string", __func__, strlen(name)); nv->value = str; str = NULL; STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links); } free(str); } static void cctl_char_handler(void *user_data, const XML_Char *str, int len) { struct cctl_devlist_data *devlist; devlist = (struct cctl_devlist_data *)user_data; sbuf_bcat(devlist->cur_sb[devlist->level], str, len); } static int cctl_devlist(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_list list; struct cctl_devlist_data devlist; struct cctl_lun *lun; XML_Parser parser; char *lun_str; int lun_len; int dump_xml = 0; int retval, c; char *backend = NULL; int verbose = 0; retval = 0; lun_len = 4096; bzero(&devlist, sizeof(devlist)); STAILQ_INIT(&devlist.lun_list); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'b': backend = strdup(optarg); break; case 'v': verbose++; break; case 'x': dump_xml = 1; break; default: break; } } retry: lun_str = malloc(lun_len); bzero(&list, sizeof(list)); list.alloc_len = lun_len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = lun_str; if (ioctl(fd, CTL_LUN_LIST, &list) == -1) { warn("%s: error issuing CTL_LUN_LIST ioctl", __func__); retval = 1; goto bailout; } if (list.status == CTL_LUN_LIST_ERROR) { warnx("%s: error returned from CTL_LUN_LIST ioctl:\n%s", __func__, list.error_str); } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { lun_len = lun_len << 1; goto retry; } if (dump_xml != 0) { printf("%s", lun_str); goto bailout; } parser = XML_ParserCreate(NULL); if (parser == NULL) { warn("%s: Unable to create XML parser", __func__); retval = 1; goto bailout; } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_element, cctl_end_element); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, lun_str, strlen(lun_str), 1); if (retval != 1) { warnx("%s: Unable to parse XML: Error %d", __func__, XML_GetErrorCode(parser)); XML_ParserFree(parser); retval = 1; goto bailout; } XML_ParserFree(parser); printf("LUN Backend %18s %4s %-16s %-16s\n", "Size (Blocks)", "BS", "Serial Number", "Device ID"); STAILQ_FOREACH(lun, &devlist.lun_list, links) { struct cctl_lun_nv *nv; if ((backend != NULL) && (strcmp(lun->backend_type, backend) != 0)) continue; printf("%3ju %-8s %18ju %4u %-16s %-16s\n", (uintmax_t)lun->lun_id, lun->backend_type, (uintmax_t)lun->size_blocks, lun->blocksize, lun->serial_number, lun->device_id); if (verbose == 0) continue; STAILQ_FOREACH(nv, &lun->attr_list, links) { printf(" %s=%s\n", nv->name, nv->value); } } bailout: free(lun_str); return (retval); } /* * Port information. */ struct cctl_port { uint64_t port_id; char *online; char *frontend_type; char *name; int pp, vp; char *target, *port, *lun_map; STAILQ_HEAD(,cctl_lun_nv) init_list; STAILQ_HEAD(,cctl_lun_nv) lun_list; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_port) links; }; struct cctl_portlist_data { int num_ports; STAILQ_HEAD(,cctl_port) port_list; struct cctl_port *cur_port; int level; uint64_t cur_id; struct sbuf *cur_sb[32]; }; static void cctl_start_pelement(void *user_data, const char *name, const char **attr) { int i; struct cctl_portlist_data *portlist; struct cctl_port *cur_port; portlist = (struct cctl_portlist_data *)user_data; cur_port = portlist->cur_port; portlist->level++; if ((u_int)portlist->level >= (sizeof(portlist->cur_sb) / sizeof(portlist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(portlist->cur_sb) / sizeof(portlist->cur_sb[0])); portlist->cur_sb[portlist->level] = sbuf_new_auto(); if (portlist->cur_sb[portlist->level] == NULL) err(1, "%s: Unable to allocate sbuf", __func__); portlist->cur_id = 0; for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { portlist->cur_id = strtoull(attr[i+1], NULL, 0); break; } } if (strcmp(name, "targ_port") == 0) { if (cur_port != NULL) errx(1, "%s: improper port element nesting", __func__); cur_port = calloc(1, sizeof(*cur_port)); if (cur_port == NULL) err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_port)); portlist->num_ports++; portlist->cur_port = cur_port; STAILQ_INIT(&cur_port->init_list); STAILQ_INIT(&cur_port->lun_list); STAILQ_INIT(&cur_port->attr_list); cur_port->port_id = portlist->cur_id; STAILQ_INSERT_TAIL(&portlist->port_list, cur_port, links); } } static void cctl_end_pelement(void *user_data, const char *name) { struct cctl_portlist_data *portlist; struct cctl_port *cur_port; char *str; portlist = (struct cctl_portlist_data *)user_data; cur_port = portlist->cur_port; if ((cur_port == NULL) && (strcmp(name, "ctlportlist") != 0)) errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name); if (portlist->cur_sb[portlist->level] == NULL) errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, portlist->level, name); if (sbuf_finish(portlist->cur_sb[portlist->level]) != 0) err(1, "%s: sbuf_finish", __func__); str = strdup(sbuf_data(portlist->cur_sb[portlist->level])); if (str == NULL) err(1, "%s can't allocate %zd bytes for string", __func__, sbuf_len(portlist->cur_sb[portlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(portlist->cur_sb[portlist->level]); portlist->cur_sb[portlist->level] = NULL; portlist->level--; if (strcmp(name, "frontend_type") == 0) { cur_port->frontend_type = str; str = NULL; } else if (strcmp(name, "port_name") == 0) { cur_port->name = str; str = NULL; } else if (strcmp(name, "online") == 0) { cur_port->online = str; str = NULL; } else if (strcmp(name, "physical_port") == 0) { cur_port->pp = strtoull(str, NULL, 0); } else if (strcmp(name, "virtual_port") == 0) { cur_port->vp = strtoull(str, NULL, 0); } else if (strcmp(name, "target") == 0) { cur_port->target = str; str = NULL; } else if (strcmp(name, "port") == 0) { cur_port->port = str; str = NULL; } else if (strcmp(name, "lun_map") == 0) { cur_port->lun_map = str; str = NULL; } else if (strcmp(name, "targ_port") == 0) { portlist->cur_port = NULL; } else if (strcmp(name, "ctlportlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); if (strcmp(name, "initiator") == 0 || strcmp(name, "lun") == 0) asprintf(&nv->name, "%ju", portlist->cur_id); else nv->name = strdup(name); if (nv->name == NULL) err(1, "%s: can't allocated %zd bytes for string", __func__, strlen(name)); nv->value = str; str = NULL; if (strcmp(name, "initiator") == 0) STAILQ_INSERT_TAIL(&cur_port->init_list, nv, links); else if (strcmp(name, "lun") == 0) STAILQ_INSERT_TAIL(&cur_port->lun_list, nv, links); else STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links); } free(str); } static void cctl_char_phandler(void *user_data, const XML_Char *str, int len) { struct cctl_portlist_data *portlist; portlist = (struct cctl_portlist_data *)user_data; sbuf_bcat(portlist->cur_sb[portlist->level], str, len); } static int cctl_portlist(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_list list; struct cctl_portlist_data portlist; struct cctl_port *port; XML_Parser parser; char *port_str; int port_len; int dump_xml = 0; int retval, c; char *frontend = NULL; uint64_t portarg = UINT64_MAX; int verbose = 0, init = 0, lun = 0, quiet = 0; retval = 0; port_len = 4096; bzero(&portlist, sizeof(portlist)); STAILQ_INIT(&portlist.port_list); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'f': frontend = strdup(optarg); break; case 'i': init++; break; case 'l': lun++; break; case 'p': portarg = strtoll(optarg, NULL, 0); break; case 'q': quiet++; break; case 'v': verbose++; break; case 'x': dump_xml = 1; break; default: break; } } retry: port_str = malloc(port_len); bzero(&list, sizeof(list)); list.alloc_len = port_len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = port_str; if (ioctl(fd, CTL_PORT_LIST, &list) == -1) { warn("%s: error issuing CTL_PORT_LIST ioctl", __func__); retval = 1; goto bailout; } if (list.status == CTL_LUN_LIST_ERROR) { warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s", __func__, list.error_str); } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { port_len = port_len << 1; goto retry; } if (dump_xml != 0) { printf("%s", port_str); goto bailout; } parser = XML_ParserCreate(NULL); if (parser == NULL) { warn("%s: Unable to create XML parser", __func__); retval = 1; goto bailout; } XML_SetUserData(parser, &portlist); XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); XML_SetCharacterDataHandler(parser, cctl_char_phandler); retval = XML_Parse(parser, port_str, strlen(port_str), 1); if (retval != 1) { warnx("%s: Unable to parse XML: Error %d", __func__, XML_GetErrorCode(parser)); XML_ParserFree(parser); retval = 1; goto bailout; } XML_ParserFree(parser); if (quiet == 0) printf("Port Online Frontend Name pp vp\n"); STAILQ_FOREACH(port, &portlist.port_list, links) { struct cctl_lun_nv *nv; if ((frontend != NULL) && (strcmp(port->frontend_type, frontend) != 0)) continue; if ((portarg != UINT64_MAX) && (portarg != port->port_id)) continue; printf("%-4ju %-6s %-8s %-8s %-2d %-2d %s\n", (uintmax_t)port->port_id, port->online, port->frontend_type, port->name, port->pp, port->vp, port->port ? port->port : ""); if (init || verbose) { if (port->target) printf(" Target: %s\n", port->target); STAILQ_FOREACH(nv, &port->init_list, links) { printf(" Initiator %s: %s\n", nv->name, nv->value); } } if (lun || verbose) { if (port->lun_map) { STAILQ_FOREACH(nv, &port->lun_list, links) printf(" LUN %s: %s\n", nv->name, nv->value); if (STAILQ_EMPTY(&port->lun_list)) printf(" No LUNs mapped\n"); } else printf(" All LUNs mapped\n"); } if (verbose) { STAILQ_FOREACH(nv, &port->attr_list, links) { printf(" %s=%s\n", nv->name, nv->value); } } } bailout: free(port_str); return (retval); } static int cctl_lunmap(int fd, int argc, char **argv, char *combinedopt) { struct ctl_lun_map lm; int retval = 0, c; retval = 0; lm.port = UINT32_MAX; lm.plun = UINT32_MAX; lm.lun = UINT32_MAX; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'p': lm.port = strtoll(optarg, NULL, 0); break; case 'l': lm.plun = strtoll(optarg, NULL, 0); break; case 'L': lm.lun = strtoll(optarg, NULL, 0); break; default: break; } } if (ioctl(fd, CTL_LUN_MAP, &lm) == -1) { warn("%s: error issuing CTL_LUN_MAP ioctl", __func__); retval = 1; } return (retval); } void usage(int error) { fprintf(error ? stderr : stdout, "Usage:\n" "Primary commands:\n" " ctladm tur [dev_id][general options]\n" " ctladm inquiry [dev_id][general options]\n" " ctladm devid [dev_id][general options]\n" " ctladm reqsense [dev_id][general options]\n" " ctladm reportluns [dev_id][general options]\n" " ctladm read [dev_id][general options] <-l lba> <-d len>\n" " <-f file|-> <-b blocksize> [-c cdbsize][-N]\n" " ctladm write [dev_id][general options] <-l lba> <-d len>\n" " <-f file|-> <-b blocksize> [-c cdbsize][-N]\n" " ctladm readcap [dev_id][general options] [-c cdbsize]\n" " ctladm modesense [dev_id][general options] <-m page|-l> [-P pc]\n" " [-d] [-S subpage] [-c cdbsize]\n" " ctladm prin [dev_id][general options] <-a action>\n" " ctladm prout [dev_id][general options] <-a action>\n" " <-r restype] [-k key] [-s sa_key]\n" " ctladm rtpg [dev_id][general options]\n" " ctladm start [dev_id][general options] [-i] [-o]\n" " ctladm stop [dev_id][general options] [-i] [-o]\n" " ctladm synccache [dev_id][general options] [-l lba]\n" " [-b blockcount] [-r] [-i] [-c cdbsize]\n" " ctladm create <-b backend> [-B blocksize] [-d device_id]\n" " [-l lun_id] [-o name=value] [-s size_bytes]\n" " [-S serial_num] [-t dev_type]\n" " ctladm remove <-b backend> <-l lun_id> [-o name=value]\n" " ctladm modify <-b backend> <-l lun_id> <-s size_bytes>\n" " ctladm devlist [-b backend] [-v] [-x]\n" " ctladm shutdown\n" " ctladm startup\n" " ctladm hardstop\n" " ctladm hardstart\n" " ctladm lunlist\n" " ctladm lunmap -p targ_port [-l pLUN] [-L cLUN]\n" " ctladm bbrread [dev_id] <-l lba> <-d datalen>\n" " ctladm delay [dev_id] <-l datamove|done> [-T oneshot|cont]\n" " [-t secs]\n" " ctladm realsync \n" " ctladm setsync [dev_id] <-i interval>\n" " ctladm getsync [dev_id]\n" " ctladm inject [dev_id] <-i action> <-p pattern> [-r lba,len]\n" " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-l | -o | [-w wwnn][-W wwpn]>\n" " [-p targ_port] [-t port_type] [-q] [-x]\n" " ctladm portlist [-f frontend] [-i] [-p targ_port] [-q] [-v] [-x]\n" " ctladm islist [-v | -x]\n" " ctladm islogout <-a | -c connection-id | -i name | -p portal>\n" " ctladm isterminate <-a | -c connection-id | -i name | -p portal>\n" " ctladm dumpooa\n" " ctladm dumpstructs\n" " ctladm help\n" "General Options:\n" "-I intiator_id : defaults to 7, used to change the initiator id\n" "-C retries : specify the number of times to retry this command\n" "-D devicename : specify the device to operate on\n" " : (default is %s)\n" "read/write options:\n" "-l lba : logical block address\n" "-d len : read/write length, in blocks\n" "-f file|- : write/read data to/from file or stdout/stdin\n" "-b blocksize : block size, in bytes\n" "-c cdbsize : specify minimum cdb size: 6, 10, 12 or 16\n" "-N : do not copy data to/from userland\n" "readcapacity options:\n" "-c cdbsize : specify minimum cdb size: 10 or 16\n" "modesense options:\n" "-m page : specify the mode page to view\n" "-l : request a list of supported pages\n" "-P pc : specify the page control value: 0-3 (current,\n" " changeable, default, saved, respectively)\n" "-d : disable block descriptors for mode sense\n" "-S subpage : specify a subpage\n" "-c cdbsize : specify minimum cdb size: 6 or 10\n" "persistent reserve in options:\n" "-a action : specify the action value: 0-2 (read key, read\n" " reservation, read capabilities, respectively)\n" "persistent reserve out options:\n" "-a action : specify the action value: 0-5 (register, reserve,\n" " release, clear, preempt, register and ignore)\n" "-k key : key value\n" "-s sa_key : service action value\n" "-r restype : specify the reservation type: 0-5(wr ex, ex ac,\n" " wr ex ro, ex ac ro, wr ex ar, ex ac ar)\n" "start/stop options:\n" "-i : set the immediate bit (CTL does not support this)\n" "-o : set the on/offline bit\n" "synccache options:\n" "-l lba : set the starting LBA\n" "-b blockcount : set the length to sync in blocks\n" "-r : set the relative addressing bit\n" "-i : set the immediate bit\n" "-c cdbsize : specify minimum cdb size: 10 or 16\n" "create options:\n" "-b backend : backend name (\"block\", \"ramdisk\", etc.)\n" "-B blocksize : LUN blocksize in bytes (some backends)\n" "-d device_id : SCSI VPD page 0x83 ID\n" "-l lun_id : requested LUN number\n" "-o name=value : backend-specific options, multiple allowed\n" "-s size_bytes : LUN size in bytes (some backends)\n" "-S serial_num : SCSI VPD page 0x80 serial number\n" "-t dev_type : SCSI device type (0=disk, 3=processor)\n" "remove options:\n" "-b backend : backend name (\"block\", \"ramdisk\", etc.)\n" "-l lun_id : LUN number to delete\n" "-o name=value : backend-specific options, multiple allowed\n" "devlist options:\n" "-b backend : list devices from specified backend only\n" "-v : be verbose, show backend attributes\n" "-x : dump raw XML\n" "delay options:\n" "-l datamove|done : delay command at datamove or done phase\n" "-T oneshot : delay one command, then resume normal completion\n" "-T cont : delay all commands\n" "-t secs : number of seconds to delay\n" "inject options:\n" "-i error_action : action to perform\n" "-p pattern : command pattern to look for\n" "-r lba,len : LBA range for pattern\n" "-s len fmt [args] : sense data for custom sense action\n" "-c : continuous operation\n" "-d delete_id : error id to delete\n" "port options:\n" "-l : list frontend ports\n" "-o on|off : turn frontend ports on or off\n" "-w wwnn : set WWNN for one frontend\n" "-W wwpn : set WWPN for one frontend\n" "-t port_type : specify fc, scsi, ioctl, internal frontend type\n" "-p targ_port : specify target port number\n" "-q : omit header in list output\n" "-x : output port list in XML format\n" "portlist options:\n" "-f fronetnd : specify frontend type\n" "-i : report target and initiators addresses\n" "-l : report LUN mapping\n" "-p targ_port : specify target port number\n" "-q : omit header in list output\n" "-v : verbose output (report all port options)\n" "-x : output port list in XML format\n" "lunmap options:\n" "-p targ_port : specify target port number\n" "-L pLUN : specify port-visible LUN\n" "-L cLUN : specify CTL LUN\n" "bbrread options:\n" "-l lba : starting LBA\n" "-d datalen : length, in bytes, to read\n", CTL_DEFAULT_DEV); } int main(int argc, char **argv) { int c; ctladm_cmdfunction command; ctladm_cmdargs cmdargs; ctladm_optret optreturn; char *device; const char *mainopt = "C:D:I:"; const char *subopt = NULL; char combinedopt[256]; int target, lun; int optstart = 2; int retval, fd; int retries; int initid; int saved_errno; retval = 0; cmdargs = CTLADM_ARG_NONE; command = CTLADM_CMD_HELP; device = NULL; fd = -1; retries = 0; target = 0; lun = 0; initid = 7; if (argc < 2) { usage(1); retval = 1; goto bailout; } /* * Get the base option. */ optreturn = getoption(option_table,argv[1], &command, &cmdargs,&subopt); if (optreturn == CC_OR_AMBIGUOUS) { warnx("ambiguous option %s", argv[1]); usage(0); exit(1); } else if (optreturn == CC_OR_NOT_FOUND) { warnx("option %s not found", argv[1]); usage(0); exit(1); } if (cmdargs & CTLADM_ARG_NEED_TL) { if ((argc < 3) || (!isdigit(argv[2][0]))) { warnx("option %s requires a target:lun argument", argv[1]); usage(0); exit(1); } retval = cctl_parse_tl(argv[2], &target, &lun); if (retval != 0) errx(1, "invalid target:lun argument %s", argv[2]); cmdargs |= CTLADM_ARG_TARG_LUN; optstart++; } /* * Ahh, getopt(3) is a pain. * * This is a gross hack. There really aren't many other good * options (excuse the pun) for parsing options in a situation like * this. getopt is kinda braindead, so you end up having to run * through the options twice, and give each invocation of getopt * the option string for the other invocation. * * You would think that you could just have two groups of options. * The first group would get parsed by the first invocation of * getopt, and the second group would get parsed by the second * invocation of getopt. It doesn't quite work out that way. When * the first invocation of getopt finishes, it leaves optind pointing * to the argument _after_ the first argument in the second group. * So when the second invocation of getopt comes around, it doesn't * recognize the first argument it gets and then bails out. * * A nice alternative would be to have a flag for getopt that says * "just keep parsing arguments even when you encounter an unknown * argument", but there isn't one. So there's no real clean way to * easily parse two sets of arguments without having one invocation * of getopt know about the other. * * Without this hack, the first invocation of getopt would work as * long as the generic arguments are first, but the second invocation * (in the subfunction) would fail in one of two ways. In the case * where you don't set optreset, it would fail because optind may be * pointing to the argument after the one it should be pointing at. * In the case where you do set optreset, and reset optind, it would * fail because getopt would run into the first set of options, which * it doesn't understand. * * All of this would "sort of" work if you could somehow figure out * whether optind had been incremented one option too far. The * mechanics of that, however, are more daunting than just giving * both invocations all of the expect options for either invocation. * * Needless to say, I wouldn't mind if someone invented a better * (non-GPL!) command line parsing interface than getopt. I * wouldn't mind if someone added more knobs to getopt to make it * work better. Who knows, I may talk myself into doing it someday, * if the standards weenies let me. As it is, it just leads to * hackery like this and causes people to avoid it in some cases. * * KDM, September 8th, 1998 */ if (subopt != NULL) sprintf(combinedopt, "%s%s", mainopt, subopt); else sprintf(combinedopt, "%s", mainopt); /* * Start getopt processing at argv[2/3], since we've already * accepted argv[1..2] as the command name, and as a possible * device name. */ optind = optstart; /* * Now we run through the argument list looking for generic * options, and ignoring options that possibly belong to * subfunctions. */ while ((c = getopt(argc, argv, combinedopt))!= -1){ switch (c) { case 'C': cmdargs |= CTLADM_ARG_RETRIES; retries = strtol(optarg, NULL, 0); break; case 'D': device = strdup(optarg); cmdargs |= CTLADM_ARG_DEVICE; break; case 'I': cmdargs |= CTLADM_ARG_INITIATOR; initid = strtol(optarg, NULL, 0); break; default: break; } } if ((cmdargs & CTLADM_ARG_INITIATOR) == 0) initid = 7; optind = optstart; optreset = 1; /* * Default to opening the CTL device for now. */ if (((cmdargs & CTLADM_ARG_DEVICE) == 0) && (command != CTLADM_CMD_HELP)) { device = strdup(CTL_DEFAULT_DEV); cmdargs |= CTLADM_ARG_DEVICE; } if ((cmdargs & CTLADM_ARG_DEVICE) && (command != CTLADM_CMD_HELP)) { fd = open(device, O_RDWR); if (fd == -1 && errno == ENOENT) { saved_errno = errno; retval = kldload("ctl"); if (retval != -1) fd = open(device, O_RDWR); else errno = saved_errno; } if (fd == -1) { fprintf(stderr, "%s: error opening %s: %s\n", argv[0], device, strerror(errno)); retval = 1; goto bailout; } } else if ((command != CTLADM_CMD_HELP) && ((cmdargs & CTLADM_ARG_DEVICE) == 0)) { fprintf(stderr, "%s: you must specify a device with the " "--device argument for this command\n", argv[0]); command = CTLADM_CMD_HELP; retval = 1; } switch (command) { case CTLADM_CMD_TUR: retval = cctl_tur(fd, target, lun, initid, retries); break; case CTLADM_CMD_INQUIRY: retval = cctl_inquiry(fd, target, lun, initid, retries); break; case CTLADM_CMD_REQ_SENSE: retval = cctl_req_sense(fd, target, lun, initid, retries); break; case CTLADM_CMD_REPORT_LUNS: retval = cctl_report_luns(fd, target, lun, initid, retries); break; case CTLADM_CMD_CREATE: retval = cctl_create_lun(fd, argc, argv, combinedopt); break; case CTLADM_CMD_RM: retval = cctl_rm_lun(fd, argc, argv, combinedopt); break; case CTLADM_CMD_DEVLIST: retval = cctl_devlist(fd, argc, argv, combinedopt); break; case CTLADM_CMD_READ: case CTLADM_CMD_WRITE: retval = cctl_read_write(fd, target, lun, initid, retries, argc, argv, combinedopt, command); break; case CTLADM_CMD_PORT: retval = cctl_port(fd, argc, argv, combinedopt); break; case CTLADM_CMD_PORTLIST: retval = cctl_portlist(fd, argc, argv, combinedopt); break; case CTLADM_CMD_LUNMAP: retval = cctl_lunmap(fd, argc, argv, combinedopt); break; case CTLADM_CMD_READCAPACITY: retval = cctl_read_capacity(fd, target, lun, initid, retries, argc, argv, combinedopt); break; case CTLADM_CMD_MODESENSE: retval = cctl_mode_sense(fd, target, lun, initid, retries, argc, argv, combinedopt); break; case CTLADM_CMD_START: case CTLADM_CMD_STOP: retval = cctl_start_stop(fd, target, lun, initid, retries, (command == CTLADM_CMD_START) ? 1 : 0, argc, argv, combinedopt); break; case CTLADM_CMD_SYNC_CACHE: retval = cctl_sync_cache(fd, target, lun, initid, retries, argc, argv, combinedopt); break; case CTLADM_CMD_SHUTDOWN: case CTLADM_CMD_STARTUP: retval = cctl_startup_shutdown(fd, target, lun, initid, command); break; case CTLADM_CMD_HARDSTOP: case CTLADM_CMD_HARDSTART: retval = cctl_hardstopstart(fd, command); break; case CTLADM_CMD_BBRREAD: retval = cctl_bbrread(fd, target, lun, initid, argc, argv, combinedopt); break; case CTLADM_CMD_LUNLIST: retval = cctl_lunlist(fd); break; case CTLADM_CMD_DELAY: retval = cctl_delay(fd, target, lun, argc, argv, combinedopt); break; case CTLADM_CMD_REALSYNC: retval = cctl_realsync(fd, argc, argv); break; case CTLADM_CMD_SETSYNC: case CTLADM_CMD_GETSYNC: retval = cctl_getsetsync(fd, target, lun, command, argc, argv, combinedopt); break; case CTLADM_CMD_ERR_INJECT: retval = cctl_error_inject(fd, target, lun, argc, argv, combinedopt); break; case CTLADM_CMD_DUMPOOA: retval = cctl_dump_ooa(fd, argc, argv); break; case CTLADM_CMD_DUMPSTRUCTS: retval = cctl_dump_structs(fd, cmdargs); break; case CTLADM_CMD_PRES_IN: retval = cctl_persistent_reserve_in(fd, target, lun, initid, argc, argv, combinedopt, retries); break; case CTLADM_CMD_PRES_OUT: retval = cctl_persistent_reserve_out(fd, target, lun, initid, argc, argv, combinedopt, retries); break; case CTLADM_CMD_INQ_VPD_DEVID: retval = cctl_inquiry_vpd_devid(fd, target, lun, initid); break; case CTLADM_CMD_RTPG: retval = cctl_report_target_port_group(fd, target, lun, initid); break; case CTLADM_CMD_MODIFY: retval = cctl_modify_lun(fd, argc, argv, combinedopt); break; case CTLADM_CMD_ISLIST: retval = cctl_islist(fd, argc, argv, combinedopt); break; case CTLADM_CMD_ISLOGOUT: retval = cctl_islogout(fd, argc, argv, combinedopt); break; case CTLADM_CMD_ISTERMINATE: retval = cctl_isterminate(fd, argc, argv, combinedopt); break; case CTLADM_CMD_HELP: default: usage(retval); break; } bailout: if (fd != -1) close(fd); exit (retval); } /* * vim: ts=8 */ Index: projects/building-blocks/usr.sbin/ctld/ctld.c =================================================================== --- projects/building-blocks/usr.sbin/ctld/ctld.c (revision 278195) +++ projects/building-blocks/usr.sbin/ctld/ctld.c (revision 278196) @@ -1,2407 +1,2418 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctld.h" #include "isns.h" bool proxy_mode = false; static volatile bool sighup_received = false; static volatile bool sigterm_received = false; static volatile bool sigalrm_received = false; static int nchildren = 0; +static uint16_t last_portal_group_tag = 0; static void usage(void) { fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); exit(1); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } struct conf * conf_new(void) { struct conf *conf; conf = calloc(1, sizeof(*conf)); if (conf == NULL) log_err(1, "calloc"); TAILQ_INIT(&conf->conf_luns); TAILQ_INIT(&conf->conf_targets); TAILQ_INIT(&conf->conf_auth_groups); TAILQ_INIT(&conf->conf_portal_groups); TAILQ_INIT(&conf->conf_isns); conf->conf_isns_period = 900; conf->conf_isns_timeout = 5; conf->conf_debug = 0; conf->conf_timeout = 60; conf->conf_maxproc = 30; return (conf); } void conf_delete(struct conf *conf) { struct lun *lun, *ltmp; struct target *targ, *tmp; struct auth_group *ag, *cagtmp; struct portal_group *pg, *cpgtmp; struct isns *is, *istmp; assert(conf->conf_pidfh == NULL); TAILQ_FOREACH_SAFE(lun, &conf->conf_luns, l_next, ltmp) lun_delete(lun); TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) target_delete(targ); TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) auth_group_delete(ag); TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) portal_group_delete(pg); TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp) isns_delete(is); free(conf->conf_pidfile_path); free(conf); } static struct auth * auth_new(struct auth_group *ag) { struct auth *auth; auth = calloc(1, sizeof(*auth)); if (auth == NULL) log_err(1, "calloc"); auth->a_auth_group = ag; TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); return (auth); } static void auth_delete(struct auth *auth) { TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); free(auth->a_user); free(auth->a_secret); free(auth->a_mutual_user); free(auth->a_mutual_secret); free(auth); } const struct auth * auth_find(const struct auth_group *ag, const char *user) { const struct auth *auth; TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { if (strcmp(auth->a_user, user) == 0) return (auth); } return (NULL); } static void auth_check_secret_length(struct auth *auth) { size_t len; len = strlen(auth->a_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too short; it should be at least 12 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too short; it should be at least 16 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (auth->a_mutual_secret != NULL) { len = strlen(auth->a_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too short; it " "should be at least 12 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too short; it should be " "at least 16 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } } } const struct auth * auth_new_chap(struct auth_group *ag, const char *user, const char *secret) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP; if (ag->ag_type != AG_TYPE_CHAP) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap\" authentication with " "other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap\" authentication with " "other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth_check_secret_length(auth); return (auth); } const struct auth * auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP_MUTUAL; if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth->a_mutual_user = checked_strdup(user2); auth->a_mutual_secret = checked_strdup(secret2); auth_check_secret_length(auth); return (auth); } const struct auth_name * auth_name_new(struct auth_group *ag, const char *name) { struct auth_name *an; an = calloc(1, sizeof(*an)); if (an == NULL) log_err(1, "calloc"); an->an_auth_group = ag; an->an_initator_name = checked_strdup(name); TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next); return (an); } static void auth_name_delete(struct auth_name *an) { TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next); free(an->an_initator_name); free(an); } bool auth_name_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_names)) return (false); return (true); } const struct auth_name * auth_name_find(const struct auth_group *ag, const char *name) { const struct auth_name *auth_name; TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) { if (strcmp(auth_name->an_initator_name, name) == 0) return (auth_name); } return (NULL); } int auth_name_check(const struct auth_group *ag, const char *initiator_name) { if (!auth_name_defined(ag)) return (0); if (auth_name_find(ag, initiator_name) == NULL) return (1); return (0); } const struct auth_portal * auth_portal_new(struct auth_group *ag, const char *portal) { struct auth_portal *ap; char *net, *mask, *str, *tmp; int len, dm, m; ap = calloc(1, sizeof(*ap)); if (ap == NULL) log_err(1, "calloc"); ap->ap_auth_group = ag; ap->ap_initator_portal = checked_strdup(portal); mask = str = checked_strdup(portal); net = strsep(&mask, "/"); if (net[0] == '[') net++; len = strlen(net); if (len == 0) goto error; if (net[len - 1] == ']') net[len - 1] = 0; if (strchr(net, ':') != NULL) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ap->ap_sa; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0) goto error; dm = 128; } else { struct sockaddr_in *sin = (struct sockaddr_in *)&ap->ap_sa; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0) goto error; dm = 32; } if (mask != NULL) { m = strtol(mask, &tmp, 0); if (m < 0 || m > dm || tmp[0] != 0) goto error; } else m = dm; ap->ap_mask = m; free(str); TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next); return (ap); error: log_errx(1, "Incorrect initiator portal '%s'", portal); return (NULL); } static void auth_portal_delete(struct auth_portal *ap) { TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next); free(ap->ap_initator_portal); free(ap); } bool auth_portal_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_portals)) return (false); return (true); } const struct auth_portal * auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss) { const struct auth_portal *ap; const uint8_t *a, *b; int i; uint8_t bmask; TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) { if (ap->ap_sa.ss_family != ss->ss_family) continue; if (ss->ss_family == AF_INET) { a = (const uint8_t *) &((const struct sockaddr_in *)ss)->sin_addr; b = (const uint8_t *) &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr; } else { a = (const uint8_t *) &((const struct sockaddr_in6 *)ss)->sin6_addr; b = (const uint8_t *) &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr; } for (i = 0; i < ap->ap_mask / 8; i++) { if (a[i] != b[i]) goto next; } if (ap->ap_mask % 8) { bmask = 0xff << (8 - (ap->ap_mask % 8)); if ((a[i] & bmask) != (b[i] & bmask)) goto next; } return (ap); next: ; } return (NULL); } int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa) { if (!auth_portal_defined(ag)) return (0); if (auth_portal_find(ag, sa) == NULL) return (1); return (0); } struct auth_group * auth_group_new(struct conf *conf, const char *name) { struct auth_group *ag; if (name != NULL) { ag = auth_group_find(conf, name); if (ag != NULL) { log_warnx("duplicated auth-group \"%s\"", name); return (NULL); } } ag = calloc(1, sizeof(*ag)); if (ag == NULL) log_err(1, "calloc"); if (name != NULL) ag->ag_name = checked_strdup(name); TAILQ_INIT(&ag->ag_auths); TAILQ_INIT(&ag->ag_names); TAILQ_INIT(&ag->ag_portals); ag->ag_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); return (ag); } void auth_group_delete(struct auth_group *ag) { struct auth *auth, *auth_tmp; struct auth_name *auth_name, *auth_name_tmp; struct auth_portal *auth_portal, *auth_portal_tmp; TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) auth_delete(auth); TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) auth_name_delete(auth_name); TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, auth_portal_tmp) auth_portal_delete(auth_portal); free(ag->ag_name); free(ag); } struct auth_group * auth_group_find(const struct conf *conf, const char *name) { struct auth_group *ag; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) return (ag); } return (NULL); } int auth_group_set_type(struct auth_group *ag, const char *str) { int type; if (strcmp(str, "none") == 0) { type = AG_TYPE_NO_AUTHENTICATION; } else if (strcmp(str, "deny") == 0) { type = AG_TYPE_DENY; } else if (strcmp(str, "chap") == 0) { type = AG_TYPE_CHAP; } else if (strcmp(str, "chap-mutual") == 0) { type = AG_TYPE_CHAP_MUTUAL; } else { if (ag->ag_name != NULL) log_warnx("invalid auth-type \"%s\" for auth-group " "\"%s\"", str, ag->ag_name); else log_warnx("invalid auth-type \"%s\" for target " "\"%s\"", str, ag->ag_target->t_name); return (1); } if (ag->ag_type != AG_TYPE_UNKNOWN && ag->ag_type != type) { if (ag->ag_name != NULL) { log_warnx("cannot set auth-type to \"%s\" for " "auth-group \"%s\"; already has a different " "type", str, ag->ag_name); } else { log_warnx("cannot set auth-type to \"%s\" for target " "\"%s\"; already has a different type", str, ag->ag_target->t_name); } return (1); } ag->ag_type = type; return (0); } static struct portal * portal_new(struct portal_group *pg) { struct portal *portal; portal = calloc(1, sizeof(*portal)); if (portal == NULL) log_err(1, "calloc"); TAILQ_INIT(&portal->p_targets); portal->p_portal_group = pg; TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); return (portal); } static void portal_delete(struct portal *portal) { TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); if (portal->p_ai != NULL) freeaddrinfo(portal->p_ai); free(portal->p_listen); free(portal); } struct portal_group * portal_group_new(struct conf *conf, const char *name) { struct portal_group *pg; pg = portal_group_find(conf, name); if (pg != NULL) { log_warnx("duplicated portal-group \"%s\"", name); return (NULL); } pg = calloc(1, sizeof(*pg)); if (pg == NULL) log_err(1, "calloc"); pg->pg_name = checked_strdup(name); TAILQ_INIT(&pg->pg_portals); pg->pg_conf = conf; - conf->conf_last_portal_group_tag++; - pg->pg_tag = conf->conf_last_portal_group_tag; + pg->pg_tag = 0; /* Assigned later in conf_apply(). */ TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); return (pg); } void portal_group_delete(struct portal_group *pg) { struct portal *portal, *tmp; TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) portal_delete(portal); free(pg->pg_name); free(pg->pg_redirection); free(pg); } struct portal_group * portal_group_find(const struct conf *conf, const char *name) { struct portal_group *pg; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (strcmp(pg->pg_name, name) == 0) return (pg); } return (NULL); } static int parse_addr_port(char *arg, const char *def_port, struct addrinfo **ai) { struct addrinfo hints; char *str, *addr, *ch; const char *port; int error, colons = 0; str = arg = strdup(arg); if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) return (1); if (arg[0] == '\0') { port = def_port; } else if (arg[0] == ':') { port = arg + 1; } else { free(str); return (1); } } else { /* * Either IPv6 address without brackets - and without * a port - or IPv4 address. Just count the colons. */ for (ch = arg; *ch != '\0'; ch++) { if (*ch == ':') colons++; } if (colons > 1) { addr = arg; port = def_port; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = def_port; else port = arg; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); free(str); return ((error != 0) ? 1 : 0); } int portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) { struct portal *portal; portal = portal_new(pg); portal->p_listen = checked_strdup(value); portal->p_iser = iser; if (parse_addr_port(portal->p_listen, "3260", &portal->p_ai)) { log_warnx("invalid listen address %s", portal->p_listen); portal_delete(portal); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple portals. */ return (0); } int isns_new(struct conf *conf, const char *addr) { struct isns *isns; isns = calloc(1, sizeof(*isns)); if (isns == NULL) log_err(1, "calloc"); isns->i_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_isns, isns, i_next); isns->i_addr = checked_strdup(addr); if (parse_addr_port(isns->i_addr, "3205", &isns->i_ai)) { log_warnx("invalid iSNS address %s", isns->i_addr); isns_delete(isns); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple servers. */ return (0); } void isns_delete(struct isns *isns) { TAILQ_REMOVE(&isns->i_conf->conf_isns, isns, i_next); free(isns->i_addr); if (isns->i_ai != NULL) freeaddrinfo(isns->i_ai); free(isns); } static int isns_do_connect(struct isns *isns) { int s; s = socket(isns->i_ai->ai_family, isns->i_ai->ai_socktype, isns->i_ai->ai_protocol); if (s < 0) { log_warn("socket(2) failed for %s", isns->i_addr); return (-1); } if (connect(s, isns->i_ai->ai_addr, isns->i_ai->ai_addrlen)) { log_warn("connect(2) failed for %s", isns->i_addr); close(s); return (-1); } return(s); } static int isns_do_register(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct target *target; struct portal *portal; struct portal_group *pg; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); isns_req_add_32(req, 2, 2); /* 2 -- iSCSI */ isns_req_add_32(req, 6, conf->conf_isns_period); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_unassigned) continue; TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 16, portal->p_ai); isns_req_add_port(req, 17, portal->p_ai); } } TAILQ_FOREACH(target, &conf->conf_targets, t_next) { isns_req_add_str(req, 32, target->t_name); isns_req_add_32(req, 33, 1); /* 1 -- Target*/ if (target->t_alias != NULL) isns_req_add_str(req, 34, target->t_alias); pg = target->t_portal_group; isns_req_add_32(req, 51, pg->pg_tag); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 49, portal->p_ai); isns_req_add_port(req, 50, portal->p_ai); } } res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS register error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_check(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRQRY, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_str(req, 1, hostname); isns_req_add_delim(req); isns_req_add(req, 2, 0, NULL); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS check error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_deregister(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVDEREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS deregister error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } void isns_register(struct isns *isns, struct isns *oldisns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets)) oldisns = isns; isns_do_deregister(oldisns, s, hostname); isns_do_register(isns, s, hostname); close(s); set_timeout(0, false); } void isns_check(struct isns *isns) { struct conf *conf = isns->i_conf; int s, res; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); res = isns_do_check(isns, s, hostname); if (res < 0) { isns_do_deregister(isns, s, hostname); isns_do_register(isns, s, hostname); } close(s); set_timeout(0, false); } void isns_deregister(struct isns *isns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) return; gethostname(hostname, sizeof(hostname)); isns_do_deregister(isns, s, hostname); close(s); set_timeout(0, false); } int portal_group_set_filter(struct portal_group *pg, const char *str) { int filter; if (strcmp(str, "none") == 0) { filter = PG_FILTER_NONE; } else if (strcmp(str, "portal") == 0) { filter = PG_FILTER_PORTAL; } else if (strcmp(str, "portal-name") == 0) { filter = PG_FILTER_PORTAL_NAME; } else if (strcmp(str, "portal-name-auth") == 0) { filter = PG_FILTER_PORTAL_NAME_AUTH; } else { log_warnx("invalid discovery-filter \"%s\" for portal-group " "\"%s\"; valid values are \"none\", \"portal\", " "\"portal-name\", and \"portal-name-auth\"", str, pg->pg_name); return (1); } if (pg->pg_discovery_filter != PG_FILTER_UNKNOWN && pg->pg_discovery_filter != filter) { log_warnx("cannot set discovery-filter to \"%s\" for " "portal-group \"%s\"; already has a different " "value", str, pg->pg_name); return (1); } pg->pg_discovery_filter = filter; return (0); } int portal_group_set_redirection(struct portal_group *pg, const char *addr) { if (pg->pg_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "portal-group \"%s\"; already defined", addr, pg->pg_name); return (1); } pg->pg_redirection = checked_strdup(addr); return (0); } static bool valid_hex(const char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': return (true); default: return (false); } } bool valid_iscsi_name(const char *name) { int i; if (strlen(name) >= MAX_NAME_LEN) { log_warnx("overlong name for target \"%s\"; max length allowed " "by iSCSI specification is %d characters", name, MAX_NAME_LEN); return (false); } /* * In the cases below, we don't return an error, just in case the admin * was right, and we're wrong. */ if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { for (i = strlen("iqn."); name[i] != '\0'; i++) { /* * XXX: We should verify UTF-8 normalisation, as defined * by 3.2.6.2: iSCSI Name Encoding. */ if (isalnum(name[i])) continue; if (name[i] == '-' || name[i] == '.' || name[i] == ':') continue; log_warnx("invalid character \"%c\" in target name " "\"%s\"; allowed characters are letters, digits, " "'-', '.', and ':'", name[i], name); break; } /* * XXX: Check more stuff: valid date and a valid reversed domain. */ } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { if (strlen(name) != strlen("eui.") + 16) log_warnx("invalid target name \"%s\"; the \"eui.\" " "should be followed by exactly 16 hexadecimal " "digits", name); for (i = strlen("eui."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { if (strlen(name) > strlen("naa.") + 32) log_warnx("invalid target name \"%s\"; the \"naa.\" " "should be followed by at most 32 hexadecimal " "digits", name); for (i = strlen("naa."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else { log_warnx("invalid target name \"%s\"; should start with " "either \".iqn\", \"eui.\", or \"naa.\"", name); } return (true); } struct target * target_new(struct conf *conf, const char *name) { struct target *targ; int i, len; targ = target_find(conf, name); if (targ != NULL) { log_warnx("duplicated target \"%s\"", name); return (NULL); } if (valid_iscsi_name(name) == false) { log_warnx("target name \"%s\" is invalid", name); return (NULL); } targ = calloc(1, sizeof(*targ)); if (targ == NULL) log_err(1, "calloc"); targ->t_name = checked_strdup(name); /* * RFC 3722 requires us to normalize the name to lowercase. */ len = strlen(name); for (i = 0; i < len; i++) targ->t_name[i] = tolower(targ->t_name[i]); targ->t_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ->t_name); free(targ->t_redirection); free(targ); } struct target * target_find(struct conf *conf, const char *name) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (strcasecmp(targ->t_name, name) == 0) return (targ); } return (NULL); } int target_set_redirection(struct target *target, const char *addr) { if (target->t_redirection != NULL) { log_warnx("cannot set redirection to \"%s\" for " "target \"%s\"; already defined", addr, target->t_name); return (1); } target->t_redirection = checked_strdup(addr); return (0); } void target_set_ctl_port(struct target *target, uint32_t value) { target->t_ctl_port = value; } struct lun * lun_new(struct conf *conf, const char *name) { struct lun *lun; lun = lun_find(conf, name); if (lun != NULL) { log_warnx("duplicated lun \"%s\"", name); return (NULL); } lun = calloc(1, sizeof(*lun)); if (lun == NULL) log_err(1, "calloc"); lun->l_conf = conf; lun->l_name = checked_strdup(name); TAILQ_INIT(&lun->l_options); TAILQ_INSERT_TAIL(&conf->conf_luns, lun, l_next); return (lun); } void lun_delete(struct lun *lun) { struct target *targ; struct lun_option *lo, *tmp; int i; TAILQ_FOREACH(targ, &lun->l_conf->conf_targets, t_next) { for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == lun) targ->t_luns[i] = NULL; } } TAILQ_REMOVE(&lun->l_conf->conf_luns, lun, l_next); TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) lun_option_delete(lo); free(lun->l_name); free(lun->l_backend); free(lun->l_device_id); free(lun->l_path); free(lun->l_scsiname); free(lun->l_serial); free(lun); } struct lun * lun_find(const struct conf *conf, const char *name) { struct lun *lun; TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { if (strcmp(lun->l_name, name) == 0) return (lun); } return (NULL); } void lun_set_backend(struct lun *lun, const char *value) { free(lun->l_backend); lun->l_backend = checked_strdup(value); } void lun_set_blocksize(struct lun *lun, size_t value) { lun->l_blocksize = value; } void lun_set_device_id(struct lun *lun, const char *value) { free(lun->l_device_id); lun->l_device_id = checked_strdup(value); } void lun_set_path(struct lun *lun, const char *value) { free(lun->l_path); lun->l_path = checked_strdup(value); } void lun_set_scsiname(struct lun *lun, const char *value) { free(lun->l_scsiname); lun->l_scsiname = checked_strdup(value); } void lun_set_serial(struct lun *lun, const char *value) { free(lun->l_serial); lun->l_serial = checked_strdup(value); } void lun_set_size(struct lun *lun, size_t value) { lun->l_size = value; } void lun_set_ctl_lun(struct lun *lun, uint32_t value) { lun->l_ctl_lun = value; } struct lun_option * lun_option_new(struct lun *lun, const char *name, const char *value) { struct lun_option *lo; lo = lun_option_find(lun, name); if (lo != NULL) { log_warnx("duplicated lun option \"%s\" for lun \"%s\"", name, lun->l_name); return (NULL); } lo = calloc(1, sizeof(*lo)); if (lo == NULL) log_err(1, "calloc"); lo->lo_name = checked_strdup(name); lo->lo_value = checked_strdup(value); lo->lo_lun = lun; TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); return (lo); } void lun_option_delete(struct lun_option *lo) { TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); free(lo->lo_name); free(lo->lo_value); free(lo); } struct lun_option * lun_option_find(const struct lun *lun, const char *name) { struct lun_option *lo; TAILQ_FOREACH(lo, &lun->l_options, lo_next) { if (strcmp(lo->lo_name, name) == 0) return (lo); } return (NULL); } void lun_option_set(struct lun_option *lo, const char *value) { free(lo->lo_value); lo->lo_value = checked_strdup(value); } static struct connection * connection_new(struct portal *portal, int fd, const char *host, const struct sockaddr *client_sa) { struct connection *conn; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); conn->conn_portal = portal; conn->conn_socket = fd; conn->conn_initiator_addr = checked_strdup(host); memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len); /* * Default values, from RFC 3720, section 12. */ conn->conn_max_data_segment_length = 8192; conn->conn_max_burst_length = 262144; conn->conn_immediate_data = true; return (conn); } #if 0 static void conf_print(struct conf *conf) { struct auth_group *ag; struct auth *auth; struct auth_name *auth_name; struct auth_portal *auth_portal; struct portal_group *pg; struct portal *portal; struct target *targ; struct lun *lun; struct lun_option *lo; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { fprintf(stderr, "auth-group %s {\n", ag->ag_name); TAILQ_FOREACH(auth, &ag->ag_auths, a_next) fprintf(stderr, "\t chap-mutual %s %s %s %s\n", auth->a_user, auth->a_secret, auth->a_mutual_user, auth->a_mutual_secret); TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) fprintf(stderr, "\t initiator-name %s\n", auth_name->an_initator_name); TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next) fprintf(stderr, "\t initiator-portal %s\n", auth_portal->an_initator_portal); fprintf(stderr, "}\n"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { fprintf(stderr, "portal-group %s {\n", pg->pg_name); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) fprintf(stderr, "\t listen %s\n", portal->p_listen); fprintf(stderr, "}\n"); } TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { fprintf(stderr, "\tlun %s {\n", lun->l_name); fprintf(stderr, "\t\tpath %s\n", lun->l_path); TAILQ_FOREACH(lo, &lun->l_options, lo_next) fprintf(stderr, "\t\toption %s %s\n", lo->lo_name, lo->lo_value); fprintf(stderr, "\t}\n"); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { fprintf(stderr, "target %s {\n", targ->t_name); if (targ->t_alias != NULL) fprintf(stderr, "\t alias %s\n", targ->t_alias); fprintf(stderr, "}\n"); } } #endif static int conf_verify_lun(struct lun *lun) { const struct lun *lun2; if (lun->l_backend == NULL) lun_set_backend(lun, "block"); if (strcmp(lun->l_backend, "block") == 0) { if (lun->l_path == NULL) { log_warnx("missing path for lun \"%s\"", lun->l_name); return (1); } } else if (strcmp(lun->l_backend, "ramdisk") == 0) { if (lun->l_size == 0) { log_warnx("missing size for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } if (lun->l_path != NULL) { log_warnx("path must not be specified " "for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } } if (lun->l_blocksize == 0) { lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); } else if (lun->l_blocksize < 0) { log_warnx("invalid blocksize for lun \"%s\"; " "must be larger than 0", lun->l_name); return (1); } if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) { log_warnx("invalid size for lun \"%s\"; " "must be multiple of blocksize", lun->l_name); return (1); } TAILQ_FOREACH(lun2, &lun->l_conf->conf_luns, l_next) { if (lun == lun2) continue; if (lun->l_path != NULL && lun2->l_path != NULL && strcmp(lun->l_path, lun2->l_path) == 0) { log_debugx("WARNING: path \"%s\" duplicated " "between lun \"%s\", and " "lun \"%s\"", lun->l_path, lun->l_name, lun2->l_name); } } return (0); } int conf_verify(struct conf *conf) { struct auth_group *ag; struct portal_group *pg; struct target *targ; struct lun *lun; bool found; int error, i; if (conf->conf_pidfile_path == NULL) conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { error = conf_verify_lun(lun); if (error != 0) return (error); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == NULL) { targ->t_auth_group = auth_group_find(conf, "default"); assert(targ->t_auth_group != NULL); } if (targ->t_portal_group == NULL) { targ->t_portal_group = portal_group_find(conf, "default"); assert(targ->t_portal_group != NULL); } found = false; for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] != NULL) found = true; } if (!found && targ->t_redirection == NULL) { log_warnx("no LUNs defined for target \"%s\"", targ->t_name); } if (found && targ->t_redirection != NULL) { log_debugx("target \"%s\" contains luns, " " but configured for redirection", targ->t_name); } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { assert(pg->pg_name != NULL); if (pg->pg_discovery_auth_group == NULL) { pg->pg_discovery_auth_group = auth_group_find(conf, "default"); assert(pg->pg_discovery_auth_group != NULL); } if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) pg->pg_discovery_filter = PG_FILTER_NONE; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_portal_group == pg) break; } if (pg->pg_redirection != NULL) { if (targ != NULL) { log_debugx("portal-group \"%s\" assigned " "to target \"%s\", but configured " "for redirection", pg->pg_name, targ->t_name); } pg->pg_unassigned = false; } else if (targ != NULL) { pg->pg_unassigned = false; } else { if (strcmp(pg->pg_name, "default") != 0) log_warnx("portal-group \"%s\" not assigned " "to any target", pg->pg_name); pg->pg_unassigned = true; } } TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name == NULL) assert(ag->ag_target != NULL); else assert(ag->ag_target == NULL); found = false; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_discovery_auth_group == ag) { found = true; break; } } if (!found && ag->ag_name != NULL && strcmp(ag->ag_name, "default") != 0 && strcmp(ag->ag_name, "no-authentication") != 0 && strcmp(ag->ag_name, "no-access") != 0) { log_warnx("auth-group \"%s\" not assigned " "to any target", ag->ag_name); } } return (0); } static int conf_apply(struct conf *oldconf, struct conf *newconf) { struct target *oldtarg, *newtarg, *tmptarg; struct lun *oldlun, *newlun, *tmplun; struct portal_group *oldpg, *newpg; struct portal *oldp, *newp; struct isns *oldns, *newns; pid_t otherpid; int changed, cumulated_error = 0, error, sockbuf; int one = 1; if (oldconf->conf_debug != newconf->conf_debug) { log_debugx("changing debug level to %d", newconf->conf_debug); log_init(newconf->conf_debug); } if (oldconf->conf_pidfh != NULL) { assert(oldconf->conf_pidfile_path != NULL); if (newconf->conf_pidfile_path != NULL && strcmp(oldconf->conf_pidfile_path, newconf->conf_pidfile_path) == 0) { newconf->conf_pidfh = oldconf->conf_pidfh; oldconf->conf_pidfh = NULL; } else { log_debugx("removing pidfile %s", oldconf->conf_pidfile_path); pidfile_remove(oldconf->conf_pidfh); oldconf->conf_pidfh = NULL; } } if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { log_debugx("opening pidfile %s", newconf->conf_pidfile_path); newconf->conf_pidfh = pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); if (newconf->conf_pidfh == NULL) { if (errno == EEXIST) log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); log_err(1, "cannot open or create pidfile \"%s\"", newconf->conf_pidfile_path); } } + /* + * Go through the new portal groups, assigning tags or preserving old. + */ + TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { + oldpg = portal_group_find(oldconf, newpg->pg_name); + if (oldpg != NULL) + newpg->pg_tag = oldpg->pg_tag; + else + newpg->pg_tag = ++last_portal_group_tag; + } + /* Deregister on removed iSNS servers. */ TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } if (newns == NULL) isns_deregister(oldns); } /* * XXX: If target or lun removal fails, we should somehow "move" * the old lun or target into newconf, so that subsequent * conf_apply() would try to remove them again. That would * be somewhat hairy, though, and lun deletion failures don't * really happen, so leave it as it is for now. */ /* * First, remove any targets present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) { newtarg = target_find(newconf, oldtarg->t_name); if (newtarg != NULL) continue; error = kernel_port_remove(oldtarg); if (error != 0) { log_warnx("failed to remove target %s", oldtarg->t_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Second, remove any LUNs present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldlun, &oldconf->conf_luns, l_next, tmplun) { newlun = lun_find(newconf, oldlun->l_name); if (newlun == NULL) { log_debugx("lun \"%s\", CTL lun %d " "not found in new configuration; " "removing", oldlun->l_name, oldlun->l_ctl_lun); error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } continue; } /* * Also remove the LUNs changed by more than size. */ changed = 0; assert(oldlun->l_backend != NULL); assert(newlun->l_backend != NULL); if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { log_debugx("backend for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (oldlun->l_blocksize != newlun->l_blocksize) { log_debugx("blocksize for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_device_id != NULL && (oldlun->l_device_id == NULL || strcmp(oldlun->l_device_id, newlun->l_device_id) != 0)) { log_debugx("device-id for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_path != NULL && (oldlun->l_path == NULL || strcmp(oldlun->l_path, newlun->l_path) != 0)) { log_debugx("path for lun \"%s\", " "CTL lun %d, changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_serial != NULL && (oldlun->l_serial == NULL || strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { log_debugx("serial for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (changed) { error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } lun_delete(oldlun); continue; } lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); } TAILQ_FOREACH_SAFE(newlun, &newconf->conf_luns, l_next, tmplun) { oldlun = lun_find(oldconf, newlun->l_name); if (oldlun != NULL) { if (newlun->l_size != oldlun->l_size || newlun->l_size == 0) { log_debugx("resizing lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); error = kernel_lun_resize(newlun); if (error != 0) { log_warnx("failed to " "resize lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); cumulated_error++; } } continue; } log_debugx("adding lun \"%s\"", newlun->l_name); error = kernel_lun_add(newlun); if (error != 0) { log_warnx("failed to add lun \"%s\"", newlun->l_name); lun_delete(newlun); cumulated_error++; } } /* * Now add new targets or modify existing ones. */ TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) { oldtarg = target_find(oldconf, newtarg->t_name); if (oldtarg == NULL) error = kernel_port_add(newtarg); else { target_set_ctl_port(newtarg, oldtarg->t_ctl_port); error = kernel_port_update(newtarg); } if (error != 0) { log_warnx("failed to %s target %s", (oldtarg == NULL) ? "add" : "update", newtarg->t_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Go through the new portals, opening the sockets as neccessary. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_unassigned) { log_debugx("not listening on portal-group \"%s\", " "not assigned to any target", newpg->pg_name); continue; } TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { /* * Try to find already open portal and reuse * the listening socket. We don't care about * what portal or portal group that was, what * matters is the listening address. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (strcmp(newp->p_listen, oldp->p_listen) == 0 && oldp->p_socket > 0) { newp->p_socket = oldp->p_socket; oldp->p_socket = 0; break; } } } if (newp->p_socket > 0) { /* * We're done with this portal. */ continue; } #ifdef ICL_KERNEL_PROXY if (proxy_mode) { newpg->pg_conf->conf_portal_id++; newp->p_id = newpg->pg_conf->conf_portal_id; log_debugx("listening on %s, portal-group " "\"%s\", portal id %d, using ICL proxy", newp->p_listen, newpg->pg_name, newp->p_id); kernel_listen(newp->p_ai, newp->p_iser, newp->p_id); continue; } #endif assert(proxy_mode == false); assert(newp->p_iser == false); log_debugx("listening on %s, portal-group \"%s\"", newp->p_listen, newpg->pg_name); newp->p_socket = socket(newp->p_ai->ai_family, newp->p_ai->ai_socktype, newp->p_ai->ai_protocol); if (newp->p_socket < 0) { log_warn("socket(2) failed for %s", newp->p_listen); cumulated_error++; continue; } sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed " "for %s", newp->p_listen); sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed " "for %s", newp->p_listen); error = setsockopt(newp->p_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); if (error != 0) { log_warn("setsockopt(SO_REUSEADDR) failed " "for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } error = bind(newp->p_socket, newp->p_ai->ai_addr, newp->p_ai->ai_addrlen); if (error != 0) { log_warn("bind(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } error = listen(newp->p_socket, -1); if (error != 0) { log_warn("listen(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } } } /* * Go through the no longer used sockets, closing them. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (oldp->p_socket <= 0) continue; log_debugx("closing socket for %s, portal-group \"%s\"", oldp->p_listen, oldpg->pg_name); close(oldp->p_socket); oldp->p_socket = 0; } } /* (Re-)Register on remaining/new iSNS servers. */ TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } isns_register(newns, oldns); } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); return (cumulated_error); } bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler_fatal(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void sigalrm_handler(int dummy __unused) { sigalrm_received = true; } void set_timeout(int timeout, int fatal) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); bzero(&itv, sizeof(itv)); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); sigalrm_received = false; return; } sigalrm_received = false; bzero(&sa, sizeof(sa)); if (fatal) sa.sa_handler = sigalrm_handler_fatal; else sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ log_debugx("setting session timeout to %d seconds", timeout); bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void handle_connection(struct portal *portal, int fd, const struct sockaddr *client_sa, bool dont_fork) { struct connection *conn; int error; pid_t pid; char host[NI_MAXHOST + 1]; struct conf *conf; conf = portal->p_portal_group->pg_conf; if (dont_fork) { log_debugx("incoming connection; not forking due to -d flag"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", conf->conf_maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) { close(fd); return; } } pidfile_close(conf->conf_pidfh); error = getnameinfo(client_sa, client_sa->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if (error != 0) log_errx(1, "getnameinfo: %s", gai_strerror(error)); log_debugx("accepted connection from %s; portal group \"%s\"", host, portal->p_portal_group->pg_name); log_set_peer_addr(host); setproctitle("%s", host); conn = connection_new(portal, fd, host, client_sa); set_timeout(conf->conf_timeout, true); kernel_capsicate(); login(conn); if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { kernel_handoff(conn); log_debugx("connection handed off to the kernel"); } else { assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); discovery(conn); } log_debugx("nothing more to do; exiting"); exit(0); } static int fd_add(int fd, fd_set *fdset, int nfds) { /* * Skip sockets which we failed to bind. */ if (fd <= 0) return (nfds); FD_SET(fd, fdset); if (fd > nfds) nfds = fd; return (nfds); } static void main_loop(struct conf *conf, bool dont_fork) { struct portal_group *pg; struct portal *portal; struct sockaddr_storage client_sa; socklen_t client_salen; #ifdef ICL_KERNEL_PROXY int connection_id; int portal_id; #endif fd_set fdset; int error, nfds, client_fd; pidfile_write(conf->conf_pidfh); for (;;) { if (sighup_received || sigterm_received || timed_out()) return; #ifdef ICL_KERNEL_PROXY if (proxy_mode) { client_salen = sizeof(client_sa); kernel_accept(&connection_id, &portal_id, (struct sockaddr *)&client_sa, &client_salen); assert(client_salen >= client_sa.ss_len); log_debugx("incoming connection, id %d, portal id %d", connection_id, portal_id); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (portal->p_id == portal_id) { goto found; } } } log_errx(1, "kernel returned invalid portal_id %d", portal_id); found: handle_connection(portal, connection_id, (struct sockaddr *)&client_sa, dont_fork); } else { #endif assert(proxy_mode == false); FD_ZERO(&fdset); nfds = 0; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) nfds = fd_add(portal->p_socket, &fdset, nfds); } error = select(nfds + 1, &fdset, NULL, NULL, NULL); if (error <= 0) { if (errno == EINTR) return; log_err(1, "select"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (!FD_ISSET(portal->p_socket, &fdset)) continue; client_salen = sizeof(client_sa); client_fd = accept(portal->p_socket, (struct sockaddr *)&client_sa, &client_salen); if (client_fd < 0) log_err(1, "accept"); assert(client_salen >= client_sa.ss_len); handle_connection(portal, client_fd, (struct sockaddr *)&client_sa, dont_fork); break; } } #ifdef ICL_KERNEL_PROXY } #endif } } static void sighup_handler(int dummy __unused) { sighup_received = true; } static void sigterm_handler(int dummy __unused) { sigterm_received = true; } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } static void register_signals(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sighup_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGHUP, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGTERM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGINT, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigchld_handler; error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } int main(int argc, char **argv) { struct conf *oldconf, *newconf, *tmpconf; struct isns *newns; const char *config_path = DEFAULT_CONFIG_PATH; int debug = 0, ch, error; bool dont_daemonize = false; while ((ch = getopt(argc, argv, "df:R")) != -1) { switch (ch) { case 'd': dont_daemonize = true; debug++; break; case 'f': config_path = optarg; break; case 'R': #ifndef ICL_KERNEL_PROXY log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY " "does not support iSER protocol"); #endif proxy_mode = true; break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); kernel_init(); oldconf = conf_new_from_kernel(); newconf = conf_new_from_file(config_path); if (newconf == NULL) log_errx(1, "configuration error; exiting"); if (debug > 0) { oldconf->conf_debug = debug; newconf->conf_debug = debug; } error = conf_apply(oldconf, newconf); if (error != 0) log_errx(1, "failed to apply configuration; exiting"); conf_delete(oldconf); oldconf = NULL; register_signals(); if (dont_daemonize == false) { log_debugx("daemonizing"); if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(newconf->conf_pidfh); exit(1); } } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); for (;;) { main_loop(newconf, dont_daemonize); if (sighup_received) { sighup_received = false; log_debugx("received SIGHUP, reloading configuration"); tmpconf = conf_new_from_file(config_path); if (tmpconf == NULL) { log_warnx("configuration error, " "continuing with old configuration"); } else { if (debug > 0) tmpconf->conf_debug = debug; oldconf = newconf; newconf = tmpconf; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to reload " "configuration"); conf_delete(oldconf); oldconf = NULL; } } else if (sigterm_received) { log_debugx("exiting on signal; " "reloading empty configuration"); - log_debugx("disabling CTL iSCSI port " + log_debugx("removing CTL iSCSI ports " "and terminating all connections"); oldconf = newconf; newconf = conf_new(); if (debug > 0) newconf->conf_debug = debug; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to apply configuration"); conf_delete(oldconf); oldconf = NULL; log_warnx("exiting on signal"); exit(0); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); if (timed_out()) { set_timeout(0, false); TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) isns_check(newns); /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) { set_timeout((newconf->conf_isns_period + 2) / 3, false); } } } } /* NOTREACHED */ } Index: projects/building-blocks/usr.sbin/ctld/ctld.h =================================================================== --- projects/building-blocks/usr.sbin/ctld/ctld.h (revision 278195) +++ projects/building-blocks/usr.sbin/ctld/ctld.h (revision 278196) @@ -1,407 +1,406 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala 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 CTLD_H #define CTLD_H #include #ifdef ICL_KERNEL_PROXY #include #endif #include #include #include #include #define DEFAULT_CONFIG_PATH "/etc/ctl.conf" #define DEFAULT_PIDFILE "/var/run/ctld.pid" #define DEFAULT_BLOCKSIZE 512 #define MAX_LUNS 1024 #define MAX_NAME_LEN 223 #define MAX_DATA_SEGMENT_LENGTH (128 * 1024) #define MAX_BURST_LENGTH 16776192 #define SOCKBUF_SIZE 1048576 struct auth { TAILQ_ENTRY(auth) a_next; struct auth_group *a_auth_group; char *a_user; char *a_secret; char *a_mutual_user; char *a_mutual_secret; }; struct auth_name { TAILQ_ENTRY(auth_name) an_next; struct auth_group *an_auth_group; char *an_initator_name; }; struct auth_portal { TAILQ_ENTRY(auth_portal) ap_next; struct auth_group *ap_auth_group; char *ap_initator_portal; struct sockaddr_storage ap_sa; int ap_mask; }; #define AG_TYPE_UNKNOWN 0 #define AG_TYPE_DENY 1 #define AG_TYPE_NO_AUTHENTICATION 2 #define AG_TYPE_CHAP 3 #define AG_TYPE_CHAP_MUTUAL 4 struct auth_group { TAILQ_ENTRY(auth_group) ag_next; struct conf *ag_conf; char *ag_name; struct target *ag_target; int ag_type; TAILQ_HEAD(, auth) ag_auths; TAILQ_HEAD(, auth_name) ag_names; TAILQ_HEAD(, auth_portal) ag_portals; }; struct portal { TAILQ_ENTRY(portal) p_next; struct portal_group *p_portal_group; bool p_iser; char *p_listen; struct addrinfo *p_ai; #ifdef ICL_KERNEL_PROXY int p_id; #endif TAILQ_HEAD(, target) p_targets; int p_socket; }; #define PG_FILTER_UNKNOWN 0 #define PG_FILTER_NONE 1 #define PG_FILTER_PORTAL 2 #define PG_FILTER_PORTAL_NAME 3 #define PG_FILTER_PORTAL_NAME_AUTH 4 struct portal_group { TAILQ_ENTRY(portal_group) pg_next; struct conf *pg_conf; char *pg_name; struct auth_group *pg_discovery_auth_group; int pg_discovery_filter; bool pg_unassigned; TAILQ_HEAD(, portal) pg_portals; char *pg_redirection; uint16_t pg_tag; }; struct lun_option { TAILQ_ENTRY(lun_option) lo_next; struct lun *lo_lun; char *lo_name; char *lo_value; }; struct lun { TAILQ_ENTRY(lun) l_next; struct conf *l_conf; TAILQ_HEAD(, lun_option) l_options; char *l_name; char *l_backend; int l_blocksize; char *l_device_id; char *l_path; char *l_scsiname; char *l_serial; int64_t l_size; int l_ctl_lun; }; struct target { TAILQ_ENTRY(target) t_next; struct conf *t_conf; struct lun *t_luns[MAX_LUNS]; struct auth_group *t_auth_group; struct portal_group *t_portal_group; char *t_name; char *t_alias; char *t_redirection; uint32_t t_ctl_port; }; struct isns { TAILQ_ENTRY(isns) i_next; struct conf *i_conf; char *i_addr; struct addrinfo *i_ai; }; struct conf { char *conf_pidfile_path; TAILQ_HEAD(, lun) conf_luns; TAILQ_HEAD(, target) conf_targets; TAILQ_HEAD(, auth_group) conf_auth_groups; TAILQ_HEAD(, portal_group) conf_portal_groups; TAILQ_HEAD(, isns) conf_isns; int conf_isns_period; int conf_isns_timeout; int conf_debug; int conf_timeout; int conf_maxproc; - uint16_t conf_last_portal_group_tag; #ifdef ICL_KERNEL_PROXY int conf_portal_id; #endif struct pidfh *conf_pidfh; bool conf_default_pg_defined; bool conf_default_ag_defined; bool conf_kernel_port_on; }; #define CONN_SESSION_TYPE_NONE 0 #define CONN_SESSION_TYPE_DISCOVERY 1 #define CONN_SESSION_TYPE_NORMAL 2 #define CONN_DIGEST_NONE 0 #define CONN_DIGEST_CRC32C 1 struct connection { struct portal *conn_portal; struct target *conn_target; int conn_socket; int conn_session_type; char *conn_initiator_name; char *conn_initiator_addr; char *conn_initiator_alias; uint8_t conn_initiator_isid[6]; struct sockaddr_storage conn_initiator_sa; uint32_t conn_cmdsn; uint32_t conn_statsn; size_t conn_max_data_segment_length; size_t conn_max_burst_length; int conn_immediate_data; int conn_header_digest; int conn_data_digest; const char *conn_user; struct chap *conn_chap; }; struct pdu { struct connection *pdu_connection; struct iscsi_bhs *pdu_bhs; char *pdu_data; size_t pdu_data_len; }; #define KEYS_MAX 1024 struct keys { char *keys_names[KEYS_MAX]; char *keys_values[KEYS_MAX]; char *keys_data; size_t keys_data_len; }; #define CHAP_CHALLENGE_LEN 1024 struct chap { unsigned char chap_id; char chap_challenge[CHAP_CHALLENGE_LEN]; char chap_response[MD5_DIGEST_LENGTH]; }; struct rchap { char *rchap_secret; unsigned char rchap_id; void *rchap_challenge; size_t rchap_challenge_len; }; struct chap *chap_new(void); char *chap_get_id(const struct chap *chap); char *chap_get_challenge(const struct chap *chap); int chap_receive(struct chap *chap, const char *response); int chap_authenticate(struct chap *chap, const char *secret); void chap_delete(struct chap *chap); struct rchap *rchap_new(const char *secret); int rchap_receive(struct rchap *rchap, const char *id, const char *challenge); char *rchap_get_response(struct rchap *rchap); void rchap_delete(struct rchap *rchap); struct conf *conf_new(void); struct conf *conf_new_from_file(const char *path); struct conf *conf_new_from_kernel(void); void conf_delete(struct conf *conf); int conf_verify(struct conf *conf); struct auth_group *auth_group_new(struct conf *conf, const char *name); void auth_group_delete(struct auth_group *ag); struct auth_group *auth_group_find(const struct conf *conf, const char *name); int auth_group_set_type(struct auth_group *ag, const char *type); const struct auth *auth_new_chap(struct auth_group *ag, const char *user, const char *secret); const struct auth *auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2); const struct auth *auth_find(const struct auth_group *ag, const char *user); const struct auth_name *auth_name_new(struct auth_group *ag, const char *initiator_name); bool auth_name_defined(const struct auth_group *ag); const struct auth_name *auth_name_find(const struct auth_group *ag, const char *initiator_name); int auth_name_check(const struct auth_group *ag, const char *initiator_name); const struct auth_portal *auth_portal_new(struct auth_group *ag, const char *initiator_portal); bool auth_portal_defined(const struct auth_group *ag); const struct auth_portal *auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *sa); int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa); struct portal_group *portal_group_new(struct conf *conf, const char *name); void portal_group_delete(struct portal_group *pg); struct portal_group *portal_group_find(const struct conf *conf, const char *name); int portal_group_add_listen(struct portal_group *pg, const char *listen, bool iser); int portal_group_set_filter(struct portal_group *pg, const char *filter); int portal_group_set_redirection(struct portal_group *pg, const char *addr); int isns_new(struct conf *conf, const char *addr); void isns_delete(struct isns *is); void isns_register(struct isns *isns, struct isns *oldisns); void isns_check(struct isns *isns); void isns_deregister(struct isns *isns); struct target *target_new(struct conf *conf, const char *name); void target_delete(struct target *target); struct target *target_find(struct conf *conf, const char *name); int target_set_redirection(struct target *target, const char *addr); void target_set_ctl_port(struct target *target, uint32_t value); struct lun *lun_new(struct conf *conf, const char *name); void lun_delete(struct lun *lun); struct lun *lun_find(const struct conf *conf, const char *name); void lun_set_backend(struct lun *lun, const char *value); void lun_set_blocksize(struct lun *lun, size_t value); void lun_set_device_id(struct lun *lun, const char *value); void lun_set_path(struct lun *lun, const char *value); void lun_set_scsiname(struct lun *lun, const char *value); void lun_set_serial(struct lun *lun, const char *value); void lun_set_size(struct lun *lun, size_t value); void lun_set_ctl_lun(struct lun *lun, uint32_t value); struct lun_option *lun_option_new(struct lun *lun, const char *name, const char *value); void lun_option_delete(struct lun_option *clo); struct lun_option *lun_option_find(const struct lun *lun, const char *name); void lun_option_set(struct lun_option *clo, const char *value); void kernel_init(void); int kernel_lun_add(struct lun *lun); int kernel_lun_resize(struct lun *lun); int kernel_lun_remove(struct lun *lun); void kernel_handoff(struct connection *conn); int kernel_port_add(struct target *targ); int kernel_port_update(struct target *targ); int kernel_port_remove(struct target *targ); void kernel_capsicate(void); #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id); void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen); void kernel_send(struct pdu *pdu); void kernel_receive(struct pdu *pdu); #endif struct keys *keys_new(void); void keys_delete(struct keys *keys); void keys_load(struct keys *keys, const struct pdu *pdu); void keys_save(struct keys *keys, struct pdu *pdu); const char *keys_find(struct keys *keys, const char *name); int keys_find_int(struct keys *keys, const char *name); void keys_add(struct keys *keys, const char *name, const char *value); void keys_add_int(struct keys *keys, const char *name, int value); struct pdu *pdu_new(struct connection *conn); struct pdu *pdu_new_response(struct pdu *request); void pdu_delete(struct pdu *pdu); void pdu_receive(struct pdu *request); void pdu_send(struct pdu *response); void login(struct connection *conn); void discovery(struct connection *conn); void log_init(int level); void log_set_peer_name(const char *name); void log_set_peer_addr(const char *addr); void log_err(int, const char *, ...) __dead2 __printflike(2, 3); void log_errx(int, const char *, ...) __dead2 __printflike(2, 3); void log_warn(const char *, ...) __printflike(1, 2); void log_warnx(const char *, ...) __printflike(1, 2); void log_debugx(const char *, ...) __printflike(1, 2); char *checked_strdup(const char *); bool valid_iscsi_name(const char *name); void set_timeout(int timeout, int fatal); bool timed_out(void); #endif /* !CTLD_H */ Index: projects/building-blocks/usr.sbin/ctld/kernel.c =================================================================== --- projects/building-blocks/usr.sbin/ctld/kernel.c (revision 278195) +++ projects/building-blocks/usr.sbin/ctld/kernel.c (revision 278196) @@ -1,1081 +1,1077 @@ /*- * Copyright (c) 2003, 2004 Silicon Graphics International Corp. * Copyright (c) 1997-2007 Kenneth D. Merry * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala * 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, * 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. * */ #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 "ctld.h" #ifdef ICL_KERNEL_PROXY #include #endif extern bool proxy_mode; static int ctl_fd = 0; void kernel_init(void) { int retval, saved_errno; ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); if (ctl_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("ctl"); if (retval != -1) ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); else errno = saved_errno; } if (ctl_fd < 0) log_err(1, "failed to open %s", CTL_DEFAULT_DEV); } /* * Name/value pair used for per-LUN attributes. */ struct cctl_lun_nv { char *name; char *value; STAILQ_ENTRY(cctl_lun_nv) links; }; /* * Backend LUN information. */ struct cctl_lun { uint64_t lun_id; char *backend_type; uint64_t size_blocks; uint32_t blocksize; char *serial_number; char *device_id; char *ctld_name; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_lun) links; }; struct cctl_port { uint32_t port_id; int cfiscsi_status; char *cfiscsi_target; uint16_t cfiscsi_portal_group_tag; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_port) links; }; struct cctl_devlist_data { int num_luns; STAILQ_HEAD(,cctl_lun) lun_list; struct cctl_lun *cur_lun; int num_ports; STAILQ_HEAD(,cctl_port) port_list; struct cctl_port *cur_port; int level; struct sbuf *cur_sb[32]; }; static void cctl_start_element(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) log_errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); devlist->cur_sb[devlist->level] = sbuf_new_auto(); if (devlist->cur_sb[devlist->level] == NULL) log_err(1, "%s: unable to allocate sbuf", __func__); if (strcmp(name, "lun") == 0) { if (cur_lun != NULL) log_errx(1, "%s: improper lun element nesting", __func__); cur_lun = calloc(1, sizeof(*cur_lun)); if (cur_lun == NULL) log_err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_lun)); devlist->num_luns++; devlist->cur_lun = cur_lun; STAILQ_INIT(&cur_lun->attr_list); STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_lun->lun_id = strtoull(attr[i+1], NULL, 0); } else { log_errx(1, "%s: invalid LUN attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_end_element(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; if ((cur_lun == NULL) && (strcmp(name, "ctllunlist") != 0)) log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name); if (devlist->cur_sb[devlist->level] == NULL) log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, devlist->level, name); sbuf_finish(devlist->cur_sb[devlist->level]); str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(devlist->cur_sb[devlist->level]); devlist->cur_sb[devlist->level] = NULL; devlist->level--; if (strcmp(name, "backend_type") == 0) { cur_lun->backend_type = str; str = NULL; } else if (strcmp(name, "size") == 0) { cur_lun->size_blocks = strtoull(str, NULL, 0); } else if (strcmp(name, "blocksize") == 0) { cur_lun->blocksize = strtoul(str, NULL, 0); } else if (strcmp(name, "serial_number") == 0) { cur_lun->serial_number = str; str = NULL; } else if (strcmp(name, "device_id") == 0) { cur_lun->device_id = str; str = NULL; } else if (strcmp(name, "ctld_name") == 0) { cur_lun->ctld_name = str; str = NULL; } else if (strcmp(name, "lun") == 0) { devlist->cur_lun = NULL; } else if (strcmp(name, "ctllunlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) log_err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); nv->name = checked_strdup(name); nv->value = str; str = NULL; STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links); } free(str); } static void cctl_start_pelement(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_port *cur_port; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; devlist->level++; if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) log_errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); devlist->cur_sb[devlist->level] = sbuf_new_auto(); if (devlist->cur_sb[devlist->level] == NULL) log_err(1, "%s: unable to allocate sbuf", __func__); if (strcmp(name, "targ_port") == 0) { if (cur_port != NULL) log_errx(1, "%s: improper port element nesting (%s)", __func__, name); cur_port = calloc(1, sizeof(*cur_port)); if (cur_port == NULL) log_err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_port)); devlist->num_ports++; devlist->cur_port = cur_port; STAILQ_INIT(&cur_port->attr_list); STAILQ_INSERT_TAIL(&devlist->port_list, cur_port, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_port->port_id = strtoul(attr[i+1], NULL, 0); } else { log_errx(1, "%s: invalid LUN attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_end_pelement(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_port *cur_port; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; if ((cur_port == NULL) && (strcmp(name, "ctlportlist") != 0)) log_errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name); if (devlist->cur_sb[devlist->level] == NULL) log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, devlist->level, name); sbuf_finish(devlist->cur_sb[devlist->level]); str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(devlist->cur_sb[devlist->level]); devlist->cur_sb[devlist->level] = NULL; devlist->level--; if (strcmp(name, "cfiscsi_target") == 0) { cur_port->cfiscsi_target = str; str = NULL; } else if (strcmp(name, "cfiscsi_status") == 0) { cur_port->cfiscsi_status = strtoul(str, NULL, 0); } else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) { cur_port->cfiscsi_portal_group_tag = strtoul(str, NULL, 0); } else if (strcmp(name, "targ_port") == 0) { devlist->cur_port = NULL; } else if (strcmp(name, "ctlportlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) log_err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); nv->name = checked_strdup(name); nv->value = str; str = NULL; STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links); } free(str); } static void cctl_char_handler(void *user_data, const XML_Char *str, int len) { struct cctl_devlist_data *devlist; devlist = (struct cctl_devlist_data *)user_data; sbuf_bcat(devlist->cur_sb[devlist->level], str, len); } struct conf * conf_new_from_kernel(void) { struct conf *conf = NULL; struct target *targ; struct lun *cl; struct lun_option *lo; struct ctl_lun_list list; struct cctl_devlist_data devlist; struct cctl_lun *lun; struct cctl_port *port; XML_Parser parser; char *str; int len, retval; bzero(&devlist, sizeof(devlist)); STAILQ_INIT(&devlist.lun_list); STAILQ_INIT(&devlist.port_list); log_debugx("obtaining previously configured CTL luns from the kernel"); str = NULL; len = 4096; retry: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) { log_warn("error issuing CTL_LUN_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_ERROR) { log_warnx("error returned from CTL_LUN_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(str); return (NULL); } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_element, cctl_end_element); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } str = NULL; len = 4096; retry_port: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) { log_warn("error issuing CTL_PORT_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_PORT_LIST_ERROR) { log_warnx("error returned from CTL_PORT_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry_port; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(str); return (NULL); } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } conf = conf_new(); STAILQ_FOREACH(port, &devlist.port_list, links) { if (port->cfiscsi_target == NULL) { log_debugx("CTL port %ju wasn't managed by ctld; " "ignoring", (uintmax_t)port->port_id); continue; } if (port->cfiscsi_status != 1) { log_debugx("CTL port %ju is not active (%d); ignoring", (uintmax_t)port->port_id, port->cfiscsi_status); continue; } targ = target_find(conf, port->cfiscsi_target); if (targ == NULL) { #if 0 log_debugx("found new kernel target %s for CTL port %ld", port->cfiscsi_target, port->port_id); #endif targ = target_new(conf, port->cfiscsi_target); if (targ == NULL) { log_warnx("target_new failed"); continue; } } } STAILQ_FOREACH(lun, &devlist.lun_list, links) { struct cctl_lun_nv *nv; if (lun->ctld_name == NULL) { log_debugx("CTL lun %ju wasn't managed by ctld; " "ignoring", (uintmax_t)lun->lun_id); continue; } cl = lun_find(conf, lun->ctld_name); if (cl != NULL) { log_warnx("found CTL lun %ju \"%s\", " "also backed by CTL lun %d; ignoring", (uintmax_t)lun->lun_id, lun->ctld_name, cl->l_ctl_lun); continue; } log_debugx("found CTL lun %ju \"%s\"", (uintmax_t)lun->lun_id, lun->ctld_name); cl = lun_new(conf, lun->ctld_name); if (cl == NULL) { log_warnx("lun_new failed"); continue; } lun_set_backend(cl, lun->backend_type); lun_set_blocksize(cl, lun->blocksize); lun_set_device_id(cl, lun->device_id); lun_set_serial(cl, lun->serial_number); lun_set_size(cl, lun->size_blocks * cl->l_blocksize); lun_set_ctl_lun(cl, lun->lun_id); STAILQ_FOREACH(nv, &lun->attr_list, links) { if (strcmp(nv->name, "file") == 0 || strcmp(nv->name, "dev") == 0) { lun_set_path(cl, nv->value); continue; } lo = lun_option_new(cl, nv->name, nv->value); if (lo == NULL) log_warnx("unable to add CTL lun option %s " "for CTL lun %ju \"%s\"", nv->name, (uintmax_t) lun->lun_id, cl->l_name); } } return (conf); } static void str_arg(struct ctl_be_arg *arg, const char *name, const char *value) { arg->namelen = strlen(name) + 1; arg->name = __DECONST(char *, name); arg->vallen = strlen(value) + 1; arg->value = __DECONST(char *, value); arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD; } int kernel_lun_add(struct lun *lun) { struct lun_option *lo; struct ctl_lun_req req; int error, i, num_options; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_CREATE; req.reqdata.create.blocksize_bytes = lun->l_blocksize; if (lun->l_size != 0) req.reqdata.create.lun_size_bytes = lun->l_size; req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; req.reqdata.create.device_type = T_DIRECT; if (lun->l_serial != NULL) { strncpy(req.reqdata.create.serial_num, lun->l_serial, sizeof(req.reqdata.create.serial_num)); req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM; } if (lun->l_device_id != NULL) { strncpy(req.reqdata.create.device_id, lun->l_device_id, sizeof(req.reqdata.create.device_id)); req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID; } if (lun->l_path != NULL) { lo = lun_option_find(lun, "file"); if (lo != NULL) { lun_option_set(lo, lun->l_path); } else { lo = lun_option_new(lun, "file", lun->l_path); assert(lo != NULL); } } lo = lun_option_find(lun, "ctld_name"); if (lo != NULL) { lun_option_set(lo, lun->l_name); } else { lo = lun_option_new(lun, "ctld_name", lun->l_name); assert(lo != NULL); } lo = lun_option_find(lun, "scsiname"); if (lo == NULL && lun->l_scsiname != NULL) { lo = lun_option_new(lun, "scsiname", lun->l_scsiname); assert(lo != NULL); } num_options = 0; TAILQ_FOREACH(lo, &lun->l_options, lo_next) num_options++; req.num_be_args = num_options; if (num_options > 0) { req.be_args = malloc(num_options * sizeof(*req.be_args)); if (req.be_args == NULL) { log_warn("error allocating %zd bytes", num_options * sizeof(*req.be_args)); return (1); } i = 0; TAILQ_FOREACH(lo, &lun->l_options, lo_next) { str_arg(&req.be_args[i], lo->lo_name, lo->lo_value); i++; } assert(i == num_options); } error = ioctl(ctl_fd, CTL_LUN_REQ, &req); free(req.be_args); if (error != 0) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN creation error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN creation warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN creation status: %d", req.status); return (1); } lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id); return (0); } int kernel_lun_resize(struct lun *lun) { struct ctl_lun_req req; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_MODIFY; req.reqdata.modify.lun_id = lun->l_ctl_lun; req.reqdata.modify.lun_size_bytes = lun->l_size; if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN modification error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN modification warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN modification status: %d", req.status); return (1); } return (0); } int kernel_lun_remove(struct lun *lun) { struct ctl_lun_req req; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_RM; req.reqdata.rm.lun_id = lun->l_ctl_lun; if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN removal error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN removal warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN removal status: %d", req.status); return (1); } return (0); } void kernel_handoff(struct connection *conn) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_HANDOFF; strlcpy(req.data.handoff.initiator_name, conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name)); strlcpy(req.data.handoff.initiator_addr, conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr)); if (conn->conn_initiator_alias != NULL) { strlcpy(req.data.handoff.initiator_alias, conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias)); } memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid, sizeof(req.data.handoff.initiator_isid)); strlcpy(req.data.handoff.target_name, conn->conn_target->t_name, sizeof(req.data.handoff.target_name)); #ifdef ICL_KERNEL_PROXY if (proxy_mode) req.data.handoff.connection_id = conn->conn_socket; else req.data.handoff.socket = conn->conn_socket; #else req.data.handoff.socket = conn->conn_socket; #endif req.data.handoff.portal_group_tag = conn->conn_portal->p_portal_group->pg_tag; if (conn->conn_header_digest == CONN_DIGEST_CRC32C) req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C; if (conn->conn_data_digest == CONN_DIGEST_CRC32C) req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C; req.data.handoff.cmdsn = conn->conn_cmdsn; req.data.handoff.statsn = conn->conn_statsn; req.data.handoff.max_recv_data_segment_length = conn->conn_max_data_segment_length; req.data.handoff.max_burst_length = conn->conn_max_burst_length; req.data.handoff.immediate_data = conn->conn_immediate_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI handoff request: " "%s; dropping connection", req.error_str); } } int kernel_port_add(struct target *targ) { struct ctl_port_entry entry; struct ctl_req req; struct ctl_lun_map lm; char tagstr[16]; int error, i; /* Create iSCSI port. */ bzero(&req, sizeof(req)); strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_CREATE; req.num_args = 4; req.args = malloc(req.num_args * sizeof(*req.args)); req.args[0].namelen = sizeof("port_id"); req.args[0].name = __DECONST(char *, "port_id"); req.args[0].vallen = sizeof(targ->t_ctl_port); req.args[0].value = &targ->t_ctl_port; req.args[0].flags = CTL_BEARG_WR; str_arg(&req.args[1], "cfiscsi_target", targ->t_name); snprintf(tagstr, sizeof(tagstr), "%d", targ->t_portal_group->pg_tag); str_arg(&req.args[2], "cfiscsi_portal_group_tag", tagstr); if (targ->t_alias) str_arg(&req.args[3], "cfiscsi_target_alias", targ->t_alias); else req.num_args--; error = ioctl(ctl_fd, CTL_PORT_REQ, &req); free(req.args); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port creation request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port creation request status %d", req.status); return (1); } /* Explicitly enable mapping to block any access except allowed. */ lm.port = targ->t_ctl_port; lm.plun = UINT32_MAX; lm.lun = 0; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); /* Map configured LUNs */ for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == NULL) continue; lm.port = targ->t_ctl_port; lm.plun = i; lm.lun = targ->t_luns[i]->l_ctl_lun; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } /* Enable port */ bzero(&entry, sizeof(entry)); entry.targ_port = targ->t_ctl_port; error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry); if (error != 0) { log_warn("CTL_ENABLE_PORT ioctl failed"); return (-1); } return (0); } int kernel_port_update(struct target *targ) { struct ctl_lun_map lm; int error, i; /* Map configured LUNs and unmap others */ for (i = 0; i < MAX_LUNS; i++) { lm.port = targ->t_ctl_port; lm.plun = i; if (targ->t_luns[i] == NULL) lm.lun = UINT32_MAX; else lm.lun = targ->t_luns[i]->l_ctl_lun; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } return (0); } int kernel_port_remove(struct target *targ) { struct ctl_req req; char tagstr[16]; int error; bzero(&req, sizeof(req)); strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_REMOVE; req.num_args = 2; req.args = malloc(req.num_args * sizeof(*req.args)); str_arg(&req.args[0], "cfiscsi_target", targ->t_name); - if (targ->t_portal_group) { - snprintf(tagstr, sizeof(tagstr), "%d", - targ->t_portal_group->pg_tag); - str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr); - } else - req.num_args--; + snprintf(tagstr, sizeof(tagstr), "%d", targ->t_portal_group->pg_tag); + str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr); error = ioctl(ctl_fd, CTL_PORT_REQ, &req); free(req.args); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port removal request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port removal request status %d", req.status); return (1); } return (0); } #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LISTEN; req.data.listen.iser = iser; req.data.listen.domain = ai->ai_family; req.data.listen.socktype = ai->ai_socktype; req.data.listen.protocol = ai->ai_protocol; req.data.listen.addr = ai->ai_addr; req.data.listen.addrlen = ai->ai_addrlen; req.data.listen.portal_id = portal_id; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI listen: %s", req.error_str); } } void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen) { struct ctl_iscsi req; struct sockaddr_storage ss; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_ACCEPT; req.data.accept.initiator_addr = (struct sockaddr *)&ss; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI accept: %s", req.error_str); } *connection_id = req.data.accept.connection_id; *portal_id = req.data.accept.portal_id; *client_salen = req.data.accept.initiator_addrlen; memcpy(client_sa, &ss, *client_salen); } void kernel_send(struct pdu *pdu) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_SEND; req.data.send.connection_id = pdu->pdu_connection->conn_socket; req.data.send.bhs = pdu->pdu_bhs; req.data.send.data_segment_len = pdu->pdu_data_len; req.data.send.data_segment = pdu->pdu_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI send: " "%s; dropping connection", req.error_str); } } void kernel_receive(struct pdu *pdu) { struct ctl_iscsi req; pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH); if (pdu->pdu_data == NULL) log_err(1, "malloc"); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_RECEIVE; req.data.receive.connection_id = pdu->pdu_connection->conn_socket; req.data.receive.bhs = pdu->pdu_bhs; req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH; req.data.receive.data_segment = pdu->pdu_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI receive: " "%s; dropping connection", req.error_str); } } #endif /* ICL_KERNEL_PROXY */ /* * XXX: I CANT INTO LATIN */ void kernel_capsicate(void) { int error; cap_rights_t rights; const unsigned long cmds[] = { CTL_ISCSI }; cap_rights_init(&rights, CAP_IOCTL); error = cap_rights_limit(ctl_fd, &rights); if (error != 0 && errno != ENOSYS) log_err(1, "cap_rights_limit"); error = cap_ioctls_limit(ctl_fd, cmds, sizeof(cmds) / sizeof(cmds[0])); if (error != 0 && errno != ENOSYS) log_err(1, "cap_ioctls_limit"); error = cap_enter(); if (error != 0 && errno != ENOSYS) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } Index: projects/building-blocks/usr.sbin/pkg/pkg.c =================================================================== --- projects/building-blocks/usr.sbin/pkg/pkg.c (revision 278195) +++ projects/building-blocks/usr.sbin/pkg/pkg.c (revision 278196) @@ -1,959 +1,969 @@ /*- * Copyright (c) 2012-2014 Baptiste Daroussin * Copyright (c) 2013 Bryan Drewery * 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 #include #include #include #include #define _WITH_GETLINE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dns_utils.h" #include "config.h" struct sig_cert { char *name; unsigned char *sig; int siglen; unsigned char *cert; int certlen; bool trusted; }; typedef enum { HASH_UNKNOWN, HASH_SHA256, } hash_t; struct fingerprint { hash_t type; char *name; char hash[BUFSIZ]; STAILQ_ENTRY(fingerprint) next; }; STAILQ_HEAD(fingerprint_list, fingerprint); static int extract_pkg_static(int fd, char *p, int sz) { struct archive *a; struct archive_entry *ae; char *end; int ret, r; ret = -1; a = archive_read_new(); if (a == NULL) { warn("archive_read_new"); return (ret); } archive_read_support_filter_all(a); archive_read_support_format_tar(a); if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { warnx("archive_read_open_fd: %s", archive_error_string(a)); goto cleanup; } ae = NULL; while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { end = strrchr(archive_entry_pathname(ae), '/'); if (end == NULL) continue; if (strcmp(end, "/pkg-static") == 0) { r = archive_read_extract(a, ae, ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); strlcpy(p, archive_entry_pathname(ae), sz); break; } } if (r == ARCHIVE_OK) ret = 0; else warnx("failed to extract pkg-static: %s", archive_error_string(a)); cleanup: archive_read_free(a); return (ret); } static int install_pkg_static(const char *path, const char *pkgpath, bool force) { int pstat; pid_t pid; switch ((pid = fork())) { case -1: return (-1); case 0: if (force) execl(path, "pkg-static", "add", "-f", pkgpath, (char *)NULL); else execl(path, "pkg-static", "add", pkgpath, (char *)NULL); _exit(1); default: break; } while (waitpid(pid, &pstat, 0) == -1) if (errno != EINTR) return (-1); if (WEXITSTATUS(pstat)) return (WEXITSTATUS(pstat)); else if (WIFSIGNALED(pstat)) return (128 & (WTERMSIG(pstat))); return (pstat); } static int fetch_to_fd(const char *url, char *path) { struct url *u; struct dns_srvinfo *mirrors, *current; struct url_stat st; FILE *remote; /* To store _https._tcp. + hostname + \0 */ int fd; int retry, max_retry; off_t done, r; time_t now, last; char buf[10240]; char zone[MAXHOSTNAMELEN + 13]; static const char *mirror_type = NULL; done = 0; last = 0; max_retry = 3; current = mirrors = NULL; remote = NULL; if (mirror_type == NULL && config_string(MIRROR_TYPE, &mirror_type) != 0) { warnx("No MIRROR_TYPE defined"); return (-1); } if ((fd = mkstemp(path)) == -1) { warn("mkstemp()"); return (-1); } retry = max_retry; - u = fetchParseURL(url); + if ((u = fetchParseURL(url)) == NULL) { + warn("fetchParseURL('%s')", url); + return (-1); + } + while (remote == NULL) { if (retry == max_retry) { if (strcmp(u->scheme, "file") != 0 && strcasecmp(mirror_type, "srv") == 0) { snprintf(zone, sizeof(zone), "_%s._tcp.%s", u->scheme, u->host); mirrors = dns_getsrvinfo(zone); current = mirrors; } } if (mirrors != NULL) { strlcpy(u->host, current->host, sizeof(u->host)); u->port = current->port; } remote = fetchXGet(u, &st, ""); if (remote == NULL) { --retry; if (retry <= 0) goto fetchfail; if (mirrors == NULL) { sleep(1); } else { current = current->next; if (current == NULL) current = mirrors; } } } while (done < st.size) { if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) break; if (write(fd, buf, r) != r) { warn("write()"); goto fetchfail; } done += r; now = time(NULL); if (now > last || done == st.size) last = now; } if (ferror(remote)) goto fetchfail; goto cleanup; fetchfail: if (fd != -1) { close(fd); fd = -1; unlink(path); } cleanup: if (remote != NULL) fclose(remote); return fd; } static struct fingerprint * parse_fingerprint(ucl_object_t *obj) { const ucl_object_t *cur; ucl_object_iter_t it = NULL; const char *function, *fp, *key; struct fingerprint *f; hash_t fct = HASH_UNKNOWN; function = fp = NULL; while ((cur = ucl_iterate_object(obj, &it, true))) { key = ucl_object_key(cur); if (cur->type != UCL_STRING) continue; if (strcasecmp(key, "function") == 0) { function = ucl_object_tostring(cur); continue; } if (strcasecmp(key, "fingerprint") == 0) { fp = ucl_object_tostring(cur); continue; } } if (fp == NULL || function == NULL) return (NULL); if (strcasecmp(function, "sha256") == 0) fct = HASH_SHA256; if (fct == HASH_UNKNOWN) { warnx("Unsupported hashing function: %s", function); return (NULL); } f = calloc(1, sizeof(struct fingerprint)); f->type = fct; strlcpy(f->hash, fp, sizeof(f->hash)); return (f); } static void free_fingerprint_list(struct fingerprint_list* list) { struct fingerprint *fingerprint, *tmp; STAILQ_FOREACH_SAFE(fingerprint, list, next, tmp) { free(fingerprint->name); free(fingerprint); } free(list); } static struct fingerprint * load_fingerprint(const char *dir, const char *filename) { ucl_object_t *obj = NULL; struct ucl_parser *p = NULL; struct fingerprint *f; char path[MAXPATHLEN]; f = NULL; snprintf(path, MAXPATHLEN, "%s/%s", dir, filename); p = ucl_parser_new(0); if (!ucl_parser_add_file(p, path)) { warnx("%s: %s", path, ucl_parser_get_error(p)); ucl_parser_free(p); return (NULL); } obj = ucl_parser_get_object(p); if (obj->type == UCL_OBJECT) f = parse_fingerprint(obj); if (f != NULL) f->name = strdup(filename); ucl_object_unref(obj); ucl_parser_free(p); return (f); } static struct fingerprint_list * load_fingerprints(const char *path, int *count) { DIR *d; struct dirent *ent; struct fingerprint *finger; struct fingerprint_list *fingerprints; *count = 0; fingerprints = calloc(1, sizeof(struct fingerprint_list)); if (fingerprints == NULL) return (NULL); STAILQ_INIT(fingerprints); - if ((d = opendir(path)) == NULL) + if ((d = opendir(path)) == NULL) { + free(fingerprints); + return (NULL); + } while ((ent = readdir(d))) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; finger = load_fingerprint(path, ent->d_name); if (finger != NULL) { STAILQ_INSERT_TAIL(fingerprints, finger, next); ++(*count); } } closedir(d); return (fingerprints); } static void sha256_hash(unsigned char hash[SHA256_DIGEST_LENGTH], char out[SHA256_DIGEST_LENGTH * 2 + 1]) { int i; for (i = 0; i < SHA256_DIGEST_LENGTH; i++) sprintf(out + (i * 2), "%02x", hash[i]); out[SHA256_DIGEST_LENGTH * 2] = '\0'; } static void sha256_buf(char *buf, size_t len, char out[SHA256_DIGEST_LENGTH * 2 + 1]) { unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; out[0] = '\0'; SHA256_Init(&sha256); SHA256_Update(&sha256, buf, len); SHA256_Final(hash, &sha256); sha256_hash(hash, out); } static int sha256_fd(int fd, char out[SHA256_DIGEST_LENGTH * 2 + 1]) { int my_fd; FILE *fp; char buffer[BUFSIZ]; unsigned char hash[SHA256_DIGEST_LENGTH]; size_t r; int ret; SHA256_CTX sha256; my_fd = -1; fp = NULL; r = 0; ret = 1; out[0] = '\0'; /* Duplicate the fd so that fclose(3) does not close it. */ if ((my_fd = dup(fd)) == -1) { warnx("dup"); goto cleanup; } if ((fp = fdopen(my_fd, "rb")) == NULL) { warnx("fdopen"); goto cleanup; } SHA256_Init(&sha256); while ((r = fread(buffer, 1, BUFSIZ, fp)) > 0) SHA256_Update(&sha256, buffer, r); if (ferror(fp) != 0) { warnx("fread"); goto cleanup; } SHA256_Final(hash, &sha256); sha256_hash(hash, out); ret = 0; cleanup: if (fp != NULL) fclose(fp); else if (my_fd != -1) close(my_fd); (void)lseek(fd, 0, SEEK_SET); return (ret); } static EVP_PKEY * load_public_key_buf(const unsigned char *cert, int certlen) { EVP_PKEY *pkey; BIO *bp; char errbuf[1024]; bp = BIO_new_mem_buf(__DECONST(void *, cert), certlen); if ((pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL)) == NULL) warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); BIO_free(bp); return (pkey); } static bool rsa_verify_cert(int fd, const unsigned char *key, int keylen, unsigned char *sig, int siglen) { EVP_MD_CTX *mdctx; EVP_PKEY *pkey; char sha256[(SHA256_DIGEST_LENGTH * 2) + 2]; char errbuf[1024]; bool ret; pkey = NULL; mdctx = NULL; ret = false; /* Compute SHA256 of the package. */ if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if ((sha256_fd(fd, sha256)) == -1) { warnx("Error creating SHA256 hash for package"); goto cleanup; } if ((pkey = load_public_key_buf(key, keylen)) == NULL) { warnx("Error reading public key"); goto cleanup; } /* Verify signature of the SHA256(pkg) is valid. */ if ((mdctx = EVP_MD_CTX_create()) == NULL) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyUpdate(mdctx, sha256, strlen(sha256)) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } if (EVP_DigestVerifyFinal(mdctx, sig, siglen) != 1) { warnx("%s", ERR_error_string(ERR_get_error(), errbuf)); goto error; } ret = true; printf("done\n"); goto cleanup; error: printf("failed\n"); cleanup: if (pkey) EVP_PKEY_free(pkey); if (mdctx) EVP_MD_CTX_destroy(mdctx); ERR_free_strings(); return (ret); } static struct sig_cert * parse_cert(int fd) { int my_fd; struct sig_cert *sc; FILE *fp; struct sbuf *buf, *sig, *cert; char *line; size_t linecap; ssize_t linelen; buf = NULL; my_fd = -1; sc = NULL; line = NULL; linecap = 0; if (lseek(fd, 0, 0) == -1) { warn("lseek"); return (NULL); } /* Duplicate the fd so that fclose(3) does not close it. */ if ((my_fd = dup(fd)) == -1) { warnx("dup"); return (NULL); } if ((fp = fdopen(my_fd, "rb")) == NULL) { warn("fdopen"); close(my_fd); return (NULL); } sig = sbuf_new_auto(); cert = sbuf_new_auto(); while ((linelen = getline(&line, &linecap, fp)) > 0) { if (strcmp(line, "SIGNATURE\n") == 0) { buf = sig; continue; } else if (strcmp(line, "CERT\n") == 0) { buf = cert; continue; } else if (strcmp(line, "END\n") == 0) { break; } if (buf != NULL) sbuf_bcat(buf, line, linelen); } fclose(fp); /* Trim out unrelated trailing newline */ sbuf_setpos(sig, sbuf_len(sig) - 1); sbuf_finish(sig); sbuf_finish(cert); sc = calloc(1, sizeof(struct sig_cert)); sc->siglen = sbuf_len(sig); sc->sig = calloc(1, sc->siglen); memcpy(sc->sig, sbuf_data(sig), sc->siglen); sc->certlen = sbuf_len(cert); sc->cert = strdup(sbuf_data(cert)); sbuf_delete(sig); sbuf_delete(cert); return (sc); } static bool verify_signature(int fd_pkg, int fd_sig) { struct fingerprint_list *trusted, *revoked; struct fingerprint *fingerprint; struct sig_cert *sc; bool ret; int trusted_count, revoked_count; const char *fingerprints; char path[MAXPATHLEN]; char hash[SHA256_DIGEST_LENGTH * 2 + 1]; sc = NULL; trusted = revoked = NULL; ret = false; /* Read and parse fingerprints. */ if (config_string(FINGERPRINTS, &fingerprints) != 0) { warnx("No CONFIG_FINGERPRINTS defined"); goto cleanup; } snprintf(path, MAXPATHLEN, "%s/trusted", fingerprints); if ((trusted = load_fingerprints(path, &trusted_count)) == NULL) { warnx("Error loading trusted certificates"); goto cleanup; } if (trusted_count == 0 || trusted == NULL) { fprintf(stderr, "No trusted certificates found.\n"); goto cleanup; } snprintf(path, MAXPATHLEN, "%s/revoked", fingerprints); if ((revoked = load_fingerprints(path, &revoked_count)) == NULL) { warnx("Error loading revoked certificates"); goto cleanup; } /* Read certificate and signature in. */ if ((sc = parse_cert(fd_sig)) == NULL) { warnx("Error parsing certificate"); goto cleanup; } /* Explicitly mark as non-trusted until proven otherwise. */ sc->trusted = false; /* Parse signature and pubkey out of the certificate */ sha256_buf(sc->cert, sc->certlen, hash); /* Check if this hash is revoked */ if (revoked != NULL) { STAILQ_FOREACH(fingerprint, revoked, next) { if (strcasecmp(fingerprint->hash, hash) == 0) { fprintf(stderr, "The package was signed with " "revoked certificate %s\n", fingerprint->name); goto cleanup; } } } STAILQ_FOREACH(fingerprint, trusted, next) { if (strcasecmp(fingerprint->hash, hash) == 0) { sc->trusted = true; sc->name = strdup(fingerprint->name); break; } } if (sc->trusted == false) { fprintf(stderr, "No trusted fingerprint found matching " "package's certificate\n"); goto cleanup; } /* Verify the signature. */ printf("Verifying signature with trusted certificate %s... ", sc->name); if (rsa_verify_cert(fd_pkg, sc->cert, sc->certlen, sc->sig, sc->siglen) == false) { fprintf(stderr, "Signature is not valid\n"); goto cleanup; } ret = true; cleanup: if (trusted) free_fingerprint_list(trusted); if (revoked) free_fingerprint_list(revoked); if (sc) { free(sc->cert); free(sc->sig); free(sc->name); free(sc); } return (ret); } static int bootstrap_pkg(bool force) { int fd_pkg, fd_sig; int ret; char url[MAXPATHLEN]; char tmppkg[MAXPATHLEN]; char tmpsig[MAXPATHLEN]; const char *packagesite; const char *signature_type; char pkgstatic[MAXPATHLEN]; fd_sig = -1; ret = -1; if (config_string(PACKAGESITE, &packagesite) != 0) { warnx("No PACKAGESITE defined"); return (-1); } if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { warnx("Error looking up SIGNATURE_TYPE"); return (-1); } printf("Bootstrapping pkg from %s, please wait...\n", packagesite); /* Support pkg+http:// for PACKAGESITE which is the new format in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has no A record. */ if (strncmp(URL_SCHEME_PREFIX, packagesite, strlen(URL_SCHEME_PREFIX)) == 0) packagesite += strlen(URL_SCHEME_PREFIX); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite); snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); if ((fd_pkg = fetch_to_fd(url, tmppkg)) == -1) goto fetchfail; if (signature_type != NULL && strcasecmp(signature_type, "FINGERPRINTS") == 0) { snprintf(tmpsig, MAXPATHLEN, "%s/pkg.txz.sig.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz.sig", packagesite); if ((fd_sig = fetch_to_fd(url, tmpsig)) == -1) { fprintf(stderr, "Signature for pkg not available.\n"); goto fetchfail; } if (verify_signature(fd_pkg, fd_sig) == false) goto cleanup; } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, tmppkg, force); goto cleanup; fetchfail: warnx("Error fetching %s: %s", url, fetchLastErrString); fprintf(stderr, "A pre-built version of pkg could not be found for " "your system.\n"); fprintf(stderr, "Consider changing PACKAGESITE or installing it from " "ports: 'ports-mgmt/pkg'.\n"); cleanup: if (fd_sig != -1) { close(fd_sig); unlink(tmpsig); } - close(fd_pkg); - unlink(tmppkg); + if (fd_pkg != -1) { + close(fd_pkg); + unlink(tmppkg); + } + return (ret); } static const char confirmation_message[] = "The package management tool is not yet installed on your system.\n" "Do you want to fetch and install it now? [y/N]: "; static const char non_interactive_message[] = "The package management tool is not yet installed on your system.\n" "Please set ASSUME_ALWAYS_YES=yes environment variable to be able to bootstrap " "in non-interactive (stdin not being a tty)\n"; static int pkg_query_yes_no(void) { int ret, c; c = getchar(); if (c == 'y' || c == 'Y') ret = 1; else ret = 0; while (c != '\n' && c != EOF) c = getchar(); return (ret); } static int bootstrap_pkg_local(const char *pkgpath, bool force) { char path[MAXPATHLEN]; char pkgstatic[MAXPATHLEN]; const char *signature_type; int fd_pkg, fd_sig, ret; fd_sig = -1; ret = -1; fd_pkg = open(pkgpath, O_RDONLY); if (fd_pkg == -1) err(EXIT_FAILURE, "Unable to open %s", pkgpath); if (config_string(SIGNATURE_TYPE, &signature_type) != 0) { warnx("Error looking up SIGNATURE_TYPE"); - return (-1); + goto cleanup; } if (signature_type != NULL && strcasecmp(signature_type, "FINGERPRINTS") == 0) { snprintf(path, sizeof(path), "%s.sig", pkgpath); if ((fd_sig = open(path, O_RDONLY)) == -1) { fprintf(stderr, "Signature for pkg not available.\n"); goto cleanup; } if (verify_signature(fd_pkg, fd_sig) == false) goto cleanup; } if ((ret = extract_pkg_static(fd_pkg, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, pkgpath, force); cleanup: close(fd_pkg); if (fd_sig != -1) close(fd_sig); return (ret); } int main(int argc, char *argv[]) { char pkgpath[MAXPATHLEN]; const char *pkgarg; bool bootstrap_only, force, yes; bootstrap_only = false; force = false; pkgarg = NULL; yes = false; snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); if (argc > 1 && strcmp(argv[1], "bootstrap") == 0) { bootstrap_only = true; if (argc == 3 && strcmp(argv[2], "-f") == 0) force = true; } if ((bootstrap_only && force) || access(pkgpath, X_OK) == -1) { /* * To allow 'pkg -N' to be used as a reliable test for whether * a system is configured to use pkg, don't bootstrap pkg * when that argument is given as argv[1]. */ if (argv[1] != NULL && strcmp(argv[1], "-N") == 0) errx(EXIT_FAILURE, "pkg is not installed"); config_init(); if (argc > 1 && strcmp(argv[1], "add") == 0) { if (argc > 2 && strcmp(argv[2], "-f") == 0) { force = true; pkgarg = argv[3]; } else pkgarg = argv[2]; if (pkgarg == NULL) { fprintf(stderr, "Path to pkg.txz required\n"); exit(EXIT_FAILURE); } if (access(pkgarg, R_OK) == -1) { fprintf(stderr, "No such file: %s\n", pkgarg); exit(EXIT_FAILURE); } if (bootstrap_pkg_local(pkgarg, force) != 0) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } /* * Do not ask for confirmation if either of stdin or stdout is * not tty. Check the environment to see if user has answer * tucked in there already. */ config_bool(ASSUME_ALWAYS_YES, &yes); if (!yes) { if (!isatty(fileno(stdin))) { fprintf(stderr, non_interactive_message); exit(EXIT_FAILURE); } printf("%s", confirmation_message); if (pkg_query_yes_no() == 0) exit(EXIT_FAILURE); } if (bootstrap_pkg(force) != 0) exit(EXIT_FAILURE); config_finish(); if (bootstrap_only) exit(EXIT_SUCCESS); } else if (bootstrap_only) { printf("pkg already bootstrapped at %s\n", pkgpath); exit(EXIT_SUCCESS); } execv(pkgpath, argv); /* NOT REACHED */ return (EXIT_FAILURE); } Index: projects/building-blocks =================================================================== --- projects/building-blocks (revision 278195) +++ projects/building-blocks (revision 278196) Property changes on: projects/building-blocks ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r278050-278194