diff --git a/dev/clock_subr/clock_subr_test_data_gen.sh b/dev/clock_subr/clock_subr_test_data_gen.sh new file mode 100755 index 000000000000..2da21a8da1bd --- /dev/null +++ b/dev/clock_subr/clock_subr_test_data_gen.sh @@ -0,0 +1,25 @@ +#!/bin/ksh + +export TZ=Etc/Universal + +datesub() { + gdate "$@" '+ FILL(%_11s,%_4Y,%_m,%_d,%w,%_H,%_M,%_S), // %a %b %e %H:%M:%S %Z %Y' +} + +( + datesub -d '1970/01/01 00:00:00' + datesub -d '1981/04/12 12:00:03' + datesub -d '2011/07/21 09:57:00' + datesub -d @2147483647 + datesub -d @2147483648 + datesub -d '2063/04/05 00:00:00' + for year in `seq 1970 1 2030`; do + datesub -d "${year}/01/01 00:00:00" + datesub -d "${year}/07/01 00:00:00" + done + for year in `seq 2000 25 2600`; do + datesub -d "$((${year} - 1))/12/31 23:59:59" + datesub -d "$((${year} + 0))/01/01 00:00:00" + datesub -d "$((${year} + 1))/01/01 00:00:00" + done +)|sort -u diff --git a/dev/clock_subr/t_clock_subr.c b/dev/clock_subr/t_clock_subr.c new file mode 100644 index 000000000000..bb03c688bf79 --- /dev/null +++ b/dev/clock_subr/t_clock_subr.c @@ -0,0 +1,309 @@ +/* $NetBSD: t_clock_subr.c,v 1.3 2017/01/13 21:30:39 christos Exp $ */ + +/* + * Copyright (c) 2016 Jonathan A. Kollasch + * 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 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. + */ + +#include +__COPYRIGHT("@(#) Copyright (c) 2016\ + Jonathan A. Kollasch. All rights reserved."); +__RCSID("$NetBSD: t_clock_subr.c,v 1.3 2017/01/13 21:30:39 christos Exp $"); + +#include +#include + +#include +#include +#include + +#include + +#include "h_macros.h" + +#define FILL(ti,ye,mo,da,wd,ho,mi,se) \ +{ .time = (ti), .clock = { .dt_year = (ye), .dt_mon = (mo), .dt_day = (da), \ + .dt_wday = (wd), .dt_hour = (ho), .dt_min = (mi), .dt_sec = (se), } } + +static struct clock_test { + time_t time; + struct clock_ymdhms clock; +} const clock_tests[] = { + FILL( 0,1970, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 1970 + FILL( 15638400,1970, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 1970 + FILL( 31536000,1971, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 1971 + FILL( 47174400,1971, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 1971 + FILL( 63072000,1972, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 1972 + FILL( 78796800,1972, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 1972 + FILL( 94694400,1973, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 1973 + FILL( 110332800,1973, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 1973 + FILL( 126230400,1974, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 1974 + FILL( 141868800,1974, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 1974 + FILL( 157766400,1975, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 1975 + FILL( 173404800,1975, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 1975 + FILL( 189302400,1976, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 1976 + FILL( 205027200,1976, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 1976 + FILL( 220924800,1977, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 1977 + FILL( 236563200,1977, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 1977 + FILL( 252460800,1978, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 1978 + FILL( 268099200,1978, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 1978 + FILL( 283996800,1979, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 1979 + FILL( 299635200,1979, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 1979 + FILL( 315532800,1980, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 1980 + FILL( 331257600,1980, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 1980 + FILL( 347155200,1981, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 1981 + FILL( 355924803,1981, 4,12,0,12, 0, 3), // Sun Apr 12 12:00:03 UTC 1981 + FILL( 362793600,1981, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 1981 + FILL( 378691200,1982, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 1982 + FILL( 394329600,1982, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 1982 + FILL( 410227200,1983, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 1983 + FILL( 425865600,1983, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 1983 + FILL( 441763200,1984, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 1984 + FILL( 457488000,1984, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 1984 + FILL( 473385600,1985, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 1985 + FILL( 489024000,1985, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 1985 + FILL( 504921600,1986, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 1986 + FILL( 520560000,1986, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 1986 + FILL( 536457600,1987, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 1987 + FILL( 552096000,1987, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 1987 + FILL( 567993600,1988, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 1988 + FILL( 583718400,1988, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 1988 + FILL( 599616000,1989, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 1989 + FILL( 615254400,1989, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 1989 + FILL( 631152000,1990, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 1990 + FILL( 646790400,1990, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 1990 + FILL( 662688000,1991, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 1991 + FILL( 678326400,1991, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 1991 + FILL( 694224000,1992, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 1992 + FILL( 709948800,1992, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 1992 + FILL( 725846400,1993, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 1993 + FILL( 741484800,1993, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 1993 + FILL( 757382400,1994, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 1994 + FILL( 773020800,1994, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 1994 + FILL( 788918400,1995, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 1995 + FILL( 804556800,1995, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 1995 + FILL( 820454400,1996, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 1996 + FILL( 836179200,1996, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 1996 + FILL( 852076800,1997, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 1997 + FILL( 867715200,1997, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 1997 + FILL( 883612800,1998, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 1998 + FILL( 899251200,1998, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 1998 + FILL( 915148800,1999, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 1999 + FILL( 930787200,1999, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 1999 + FILL( 946684799,1999,12,31,5,23,59,59), // Fri Dec 31 23:59:59 UTC 1999 + FILL( 946684800,2000, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2000 + FILL( 962409600,2000, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 2000 + FILL( 978307200,2001, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2001 + FILL( 993945600,2001, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 2001 + FILL( 1009843200,2002, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2002 + FILL( 1025481600,2002, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 2002 + FILL( 1041379200,2003, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2003 + FILL( 1057017600,2003, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 2003 + FILL( 1072915200,2004, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2004 + FILL( 1088640000,2004, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 2004 + FILL( 1104537600,2005, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2005 + FILL( 1120176000,2005, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 2005 + FILL( 1136073600,2006, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2006 + FILL( 1151712000,2006, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 2006 + FILL( 1167609600,2007, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2007 + FILL( 1183248000,2007, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 2007 + FILL( 1199145600,2008, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2008 + FILL( 1214870400,2008, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 2008 + FILL( 1230768000,2009, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2009 + FILL( 1246406400,2009, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 2009 + FILL( 1262304000,2010, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2010 + FILL( 1277942400,2010, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 2010 + FILL( 1293840000,2011, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2011 + FILL( 1309478400,2011, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 2011 + FILL( 1311242220,2011, 7,21,4, 9,57, 0), // Thu Jul 21 09:57:00 UTC 2011 + FILL( 1325376000,2012, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2012 + FILL( 1341100800,2012, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 2012 + FILL( 1356998400,2013, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2013 + FILL( 1372636800,2013, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 2013 + FILL( 1388534400,2014, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2014 + FILL( 1404172800,2014, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 2014 + FILL( 1420070400,2015, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2015 + FILL( 1435708800,2015, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 2015 + FILL( 1451606400,2016, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2016 + FILL( 1467331200,2016, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 2016 + FILL( 1483228800,2017, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2017 + FILL( 1498867200,2017, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 2017 + FILL( 1514764800,2018, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2018 + FILL( 1530403200,2018, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 2018 + FILL( 1546300800,2019, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2019 + FILL( 1561939200,2019, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 2019 + FILL( 1577836800,2020, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2020 + FILL( 1593561600,2020, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 2020 + FILL( 1609459200,2021, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2021 + FILL( 1625097600,2021, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 2021 + FILL( 1640995200,2022, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2022 + FILL( 1656633600,2022, 7, 1,5, 0, 0, 0), // Fri Jul 1 00:00:00 UTC 2022 + FILL( 1672531200,2023, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2023 + FILL( 1688169600,2023, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 2023 + FILL( 1704067200,2024, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2024 + FILL( 1719792000,2024, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 2024 + FILL( 1735689599,2024,12,31,2,23,59,59), // Tue Dec 31 23:59:59 UTC 2024 + FILL( 1735689600,2025, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2025 + FILL( 1751328000,2025, 7, 1,2, 0, 0, 0), // Tue Jul 1 00:00:00 UTC 2025 + FILL( 1767225600,2026, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2026 + FILL( 1782864000,2026, 7, 1,3, 0, 0, 0), // Wed Jul 1 00:00:00 UTC 2026 + FILL( 1798761600,2027, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2027 + FILL( 1814400000,2027, 7, 1,4, 0, 0, 0), // Thu Jul 1 00:00:00 UTC 2027 + FILL( 1830297600,2028, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2028 + FILL( 1846022400,2028, 7, 1,6, 0, 0, 0), // Sat Jul 1 00:00:00 UTC 2028 + FILL( 1861920000,2029, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2029 + FILL( 1877558400,2029, 7, 1,0, 0, 0, 0), // Sun Jul 1 00:00:00 UTC 2029 + FILL( 1893456000,2030, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2030 + FILL( 1909094400,2030, 7, 1,1, 0, 0, 0), // Mon Jul 1 00:00:00 UTC 2030 + FILL( 2147483647,2038, 1,19,2, 3,14, 7), // Tue Jan 19 03:14:07 UTC 2038 + FILL( 2147483648,2038, 1,19,2, 3,14, 8), // Tue Jan 19 03:14:08 UTC 2038 + FILL( 2524607999,2049,12,31,5,23,59,59), // Fri Dec 31 23:59:59 UTC 2049 + FILL( 2524608000,2050, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2050 + FILL( 2556144000,2051, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2051 + FILL( 2942956800,2063, 4, 5,4, 0, 0, 0), // Thu Apr 5 00:00:00 UTC 2063 + FILL( 3313526399,2074,12,31,1,23,59,59), // Mon Dec 31 23:59:59 UTC 2074 + FILL( 3313526400,2075, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2075 + FILL( 3345062400,2076, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2076 + FILL( 4102444799,2099,12,31,4,23,59,59), // Thu Dec 31 23:59:59 UTC 2099 + FILL( 4102444800,2100, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2100 + FILL( 4133980800,2101, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2101 + FILL( 4891363199,2124,12,31,0,23,59,59), // Sun Dec 31 23:59:59 UTC 2124 + FILL( 4891363200,2125, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2125 + FILL( 4922899200,2126, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2126 + FILL( 5680281599,2149,12,31,3,23,59,59), // Wed Dec 31 23:59:59 UTC 2149 + FILL( 5680281600,2150, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2150 + FILL( 5711817600,2151, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2151 + FILL( 6469199999,2174,12,31,6,23,59,59), // Sat Dec 31 23:59:59 UTC 2174 + FILL( 6469200000,2175, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2175 + FILL( 6500736000,2176, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2176 + FILL( 7258118399,2199,12,31,2,23,59,59), // Tue Dec 31 23:59:59 UTC 2199 + FILL( 7258118400,2200, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2200 + FILL( 7289654400,2201, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2201 + FILL( 8047036799,2224,12,31,5,23,59,59), // Fri Dec 31 23:59:59 UTC 2224 + FILL( 8047036800,2225, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2225 + FILL( 8078572800,2226, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2226 + FILL( 8835955199,2249,12,31,1,23,59,59), // Mon Dec 31 23:59:59 UTC 2249 + FILL( 8835955200,2250, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2250 + FILL( 8867491200,2251, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2251 + FILL( 9624873599,2274,12,31,4,23,59,59), // Thu Dec 31 23:59:59 UTC 2274 + FILL( 9624873600,2275, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2275 + FILL( 9656409600,2276, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2276 + FILL(10413791999,2299,12,31,0,23,59,59), // Sun Dec 31 23:59:59 UTC 2299 + FILL(10413792000,2300, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2300 + FILL(10445328000,2301, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2301 + FILL(11202710399,2324,12,31,3,23,59,59), // Wed Dec 31 23:59:59 UTC 2324 + FILL(11202710400,2325, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2325 + FILL(11234246400,2326, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2326 + FILL(11991628799,2349,12,31,6,23,59,59), // Sat Dec 31 23:59:59 UTC 2349 + FILL(11991628800,2350, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2350 + FILL(12023164800,2351, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2351 + FILL(12780547199,2374,12,31,2,23,59,59), // Tue Dec 31 23:59:59 UTC 2374 + FILL(12780547200,2375, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2375 + FILL(12812083200,2376, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2376 + FILL(13569465599,2399,12,31,5,23,59,59), // Fri Dec 31 23:59:59 UTC 2399 + FILL(13569465600,2400, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2400 + FILL(13601088000,2401, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2401 + FILL(14358470399,2424,12,31,2,23,59,59), // Tue Dec 31 23:59:59 UTC 2424 + FILL(14358470400,2425, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2425 + FILL(14390006400,2426, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2426 + FILL(15147388799,2449,12,31,5,23,59,59), // Fri Dec 31 23:59:59 UTC 2449 + FILL(15147388800,2450, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2450 + FILL(15178924800,2451, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2451 + FILL(15936307199,2474,12,31,1,23,59,59), // Mon Dec 31 23:59:59 UTC 2474 + FILL(15936307200,2475, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2475 + FILL(15967843200,2476, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2476 + FILL(16725225599,2499,12,31,4,23,59,59), // Thu Dec 31 23:59:59 UTC 2499 + FILL(16725225600,2500, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2500 + FILL(16756761600,2501, 1, 1,6, 0, 0, 0), // Sat Jan 1 00:00:00 UTC 2501 + FILL(17514143999,2524,12,31,0,23,59,59), // Sun Dec 31 23:59:59 UTC 2524 + FILL(17514144000,2525, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2525 + FILL(17545680000,2526, 1, 1,2, 0, 0, 0), // Tue Jan 1 00:00:00 UTC 2526 + FILL(18303062399,2549,12,31,3,23,59,59), // Wed Dec 31 23:59:59 UTC 2549 + FILL(18303062400,2550, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2550 + FILL(18334598400,2551, 1, 1,5, 0, 0, 0), // Fri Jan 1 00:00:00 UTC 2551 + FILL(19091980799,2574,12,31,6,23,59,59), // Sat Dec 31 23:59:59 UTC 2574 + FILL(19091980800,2575, 1, 1,0, 0, 0, 0), // Sun Jan 1 00:00:00 UTC 2575 + FILL(19123516800,2576, 1, 1,1, 0, 0, 0), // Mon Jan 1 00:00:00 UTC 2576 + FILL(19880899199,2599,12,31,2,23,59,59), // Tue Dec 31 23:59:59 UTC 2599 + FILL(19880899200,2600, 1, 1,3, 0, 0, 0), // Wed Jan 1 00:00:00 UTC 2600 + FILL(19912435200,2601, 1, 1,4, 0, 0, 0), // Thu Jan 1 00:00:00 UTC 2601 +}; +#undef FILL + +ATF_TC(ymdhms_to_secs); +ATF_TC_HEAD(ymdhms_to_secs, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check clock_ymdhms_to_secs"); +} +ATF_TC_BODY(ymdhms_to_secs, tc) +{ + time_t secs; + size_t i; + + for (i = 0; i < __arraycount(clock_tests); i++) { + secs = clock_ymdhms_to_secs(__UNCONST(&clock_tests[i].clock)); + ATF_CHECK_EQ_MSG(clock_tests[i].time, secs, "%jd != %jd", + (intmax_t)clock_tests[i].time, (intmax_t)secs); + } +} + +ATF_TC(secs_to_ymdhms); +ATF_TC_HEAD(secs_to_ymdhms, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check clock_secs_to_ymdhms"); +} +ATF_TC_BODY(secs_to_ymdhms, tc) +{ + struct clock_ymdhms ymdhms; + size_t i; + +#define CHECK_FIELD(f) \ + ATF_CHECK_EQ_MSG(ymdhms.dt_##f, clock_tests[i].clock.dt_##f, \ + "%jd != %jd for %jd", (intmax_t)ymdhms.dt_##f, \ + (intmax_t)clock_tests[i].clock.dt_##f, \ + (intmax_t)clock_tests[i].time) + + for (i = 0; i < __arraycount(clock_tests); i++) { + clock_secs_to_ymdhms(clock_tests[i].time, &ymdhms); + CHECK_FIELD(year); + CHECK_FIELD(mon); + CHECK_FIELD(day); + CHECK_FIELD(wday); + CHECK_FIELD(hour); + CHECK_FIELD(min); + CHECK_FIELD(sec); + } +#undef CHECK_FIELD +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, ymdhms_to_secs); + ATF_TP_ADD_TC(tp, secs_to_ymdhms); + + return atf_no_error(); +} diff --git a/kernel/arch/amd64/t_ptrace_wait.c b/kernel/arch/amd64/t_ptrace_wait.c new file mode 100644 index 000000000000..5adbb0e94554 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_wait.c @@ -0,0 +1,1388 @@ +/* $NetBSD: t_ptrace_wait.c,v 1.9 2017/01/13 21:30:41 christos Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.9 2017/01/13 21:30:41 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "h_macros.h" + +#include "../../t_ptrace_wait.h" + + +#if defined(HAVE_GPREGS) +ATF_TC(regs1); +ATF_TC_HEAD(regs1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_GETREGS and iterate over General Purpose registers"); +} + +ATF_TC_BODY(regs1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct reg r; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Call GETREGS for the child process\n"); + ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); + + printf("RAX=%#" PRIxREGISTER "\n", r.regs[_REG_RAX]); + printf("RBX=%#" PRIxREGISTER "\n", r.regs[_REG_RBX]); + printf("RCX=%#" PRIxREGISTER "\n", r.regs[_REG_RCX]); + printf("RDX=%#" PRIxREGISTER "\n", r.regs[_REG_RDX]); + + printf("RDI=%#" PRIxREGISTER "\n", r.regs[_REG_RDI]); + printf("RSI=%#" PRIxREGISTER "\n", r.regs[_REG_RSI]); + + printf("GS=%#" PRIxREGISTER "\n", r.regs[_REG_GS]); + printf("FS=%#" PRIxREGISTER "\n", r.regs[_REG_FS]); + printf("ES=%#" PRIxREGISTER "\n", r.regs[_REG_ES]); + printf("DS=%#" PRIxREGISTER "\n", r.regs[_REG_DS]); + printf("CS=%#" PRIxREGISTER "\n", r.regs[_REG_CS]); + printf("SS=%#" PRIxREGISTER "\n", r.regs[_REG_SS]); + + printf("RSP=%#" PRIxREGISTER "\n", r.regs[_REG_RSP]); + printf("RIP=%#" PRIxREGISTER "\n", r.regs[_REG_RIP]); + + printf("RFLAGS=%#" PRIxREGISTER "\n", r.regs[_REG_RFLAGS]); + + printf("R8=%#" PRIxREGISTER "\n", r.regs[_REG_R8]); + printf("R9=%#" PRIxREGISTER "\n", r.regs[_REG_R9]); + printf("R10=%#" PRIxREGISTER "\n", r.regs[_REG_R10]); + printf("R11=%#" PRIxREGISTER "\n", r.regs[_REG_R11]); + printf("R12=%#" PRIxREGISTER "\n", r.regs[_REG_R12]); + printf("R13=%#" PRIxREGISTER "\n", r.regs[_REG_R13]); + printf("R14=%#" PRIxREGISTER "\n", r.regs[_REG_R14]); + printf("R15=%#" PRIxREGISTER "\n", r.regs[_REG_R15]); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_count); +ATF_TC_HEAD(watchpoint_count, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and assert four available watchpoints"); +} + +ATF_TC_BODY(watchpoint_count, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + int N; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Call GETREGS for the child process\n"); + ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); + printf("Reported %d watchpoints\n", N); + + ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_read); +ATF_TC_HEAD(watchpoint_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and assert four available watchpoints"); +} + +ATF_TC_BODY(watchpoint_read, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + int i, N; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Call GETREGS for the child process\n"); + ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); + + ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); + + for (i = 0; i < N; i++) { + printf("Before reading watchpoint %d\n", i); + pw.pw_index = i; + ATF_REQUIRE(ptrace(PT_READ_WATCHPOINT, child, &pw, len) != -1); + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + } + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_write_unmodified); +ATF_TC_HEAD(watchpoint_write_unmodified, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and assert functional write of " + "unmodified data"); +} + +ATF_TC_BODY(watchpoint_write_unmodified, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + int i, N; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Call GETREGS for the child process\n"); + ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); + + ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); + + for (i = 0; i < N; i++) { + printf("Before reading watchpoint %d\n", i); + pw.pw_index = i; + ATF_REQUIRE(ptrace(PT_READ_WATCHPOINT, child, &pw, len) != -1); + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d (unmodified)\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) + != -1); + } + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_code0); +ATF_TC_HEAD(watchpoint_trap_code0, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 0"); +} + +ATF_TC_BODY(watchpoint_trap_code0, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 0; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_lwpid = 0; + pw.pw_md.md_address = (void *)check_happy; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + pw.pw_md.md_address = NULL; + printf("Before writing watchpoint %d (disable it)\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_code1); +ATF_TC_HEAD(watchpoint_trap_code1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 1"); +} + +ATF_TC_BODY(watchpoint_trap_code1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 1; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_lwpid = 0; + pw.pw_md.md_address = (void *)check_happy; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + pw.pw_md.md_address = NULL; + printf("Before writing watchpoint %d (disable it)\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_code2); +ATF_TC_HEAD(watchpoint_trap_code2, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 2"); +} + +ATF_TC_BODY(watchpoint_trap_code2, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 2; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_lwpid = 0; + pw.pw_md.md_address = (void *)check_happy; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + pw.pw_md.md_address = NULL; + printf("Before writing watchpoint %d (disable it)\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_code3); +ATF_TC_HEAD(watchpoint_trap_code3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 3"); +} + +ATF_TC_BODY(watchpoint_trap_code3, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 3; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_lwpid = 0; + pw.pw_md.md_address = (void *)check_happy; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + pw.pw_md.md_address = NULL; + printf("Before writing watchpoint %d (disable it)\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_write0); +ATF_TC_HEAD(watchpoint_trap_data_write0, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 0"); +} + +ATF_TC_BODY(watchpoint_trap_data_write0, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 0; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + ++watchme; + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = 0; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_write1); +ATF_TC_HEAD(watchpoint_trap_data_write1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 1"); +} + +ATF_TC_BODY(watchpoint_trap_data_write1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 1; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + ++watchme; + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_write2); +ATF_TC_HEAD(watchpoint_trap_data_write2, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 2"); +} + +ATF_TC_BODY(watchpoint_trap_data_write2, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 2; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + ++watchme; + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_write3); +ATF_TC_HEAD(watchpoint_trap_data_write3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 3"); +} + +ATF_TC_BODY(watchpoint_trap_data_write3, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 3; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + ++watchme; + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_rw0); +ATF_TC_HEAD(watchpoint_trap_data_rw0, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 0"); +} + +ATF_TC_BODY(watchpoint_trap_data_rw0, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 0; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("watchme=%d\n", watchme); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_rw1); +ATF_TC_HEAD(watchpoint_trap_data_rw1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 1"); +} + +ATF_TC_BODY(watchpoint_trap_data_rw1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 1; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("watchme=%d\n", watchme); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_rw2); +ATF_TC_HEAD(watchpoint_trap_data_rw2, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 2"); +} + +ATF_TC_BODY(watchpoint_trap_data_rw2, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 2; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("watchme=%d\n", watchme); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(__HAVE_PTRACE_WATCHPOINTS) +ATF_TC(watchpoint_trap_data_rw3); +ATF_TC_HEAD(watchpoint_trap_data_rw3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 3"); +} + +ATF_TC_BODY(watchpoint_trap_data_rw3, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int i = 3; + struct ptrace_watchpoint pw; + int len = sizeof(pw); + int watchme = 1234; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("watchme=%d\n", watchme); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Preparing code watchpoint trap %d\n", i); + + pw.pw_index = i; + pw.pw_md.md_address = &watchme; + pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; + pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; + + printf("struct ptrace {\n"); + printf("\t.pw_index=%d\n", pw.pw_index); + printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); + printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); + printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); + printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); + printf("}\n"); + + printf("Before writing watchpoint %d\n", i); + ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); + + printf("Before resuming the child process where it left off " + "and without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +ATF_TP_ADD_TCS(tp) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1); + + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_count); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_read); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_write_unmodified); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code0); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code1); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code2); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code3); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write0); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write1); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write2); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write3); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw0); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw1); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw2); + ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw3); + + return atf_no_error(); +} diff --git a/kernel/arch/amd64/t_ptrace_wait3.c b/kernel/arch/amd64/t_ptrace_wait3.c new file mode 100644 index 000000000000..c5dfe7358738 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_wait3.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT3 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_wait4.c b/kernel/arch/amd64/t_ptrace_wait4.c new file mode 100644 index 000000000000..7e1fed95ee62 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_wait4.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT4 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_wait6.c b/kernel/arch/amd64/t_ptrace_wait6.c new file mode 100644 index 000000000000..601efe3f3d62 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_wait6.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT6 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_waitid.c b/kernel/arch/amd64/t_ptrace_waitid.c new file mode 100644 index 000000000000..5ec17a9b8d24 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_waitid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAITID +#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_waitpid.c b/kernel/arch/amd64/t_ptrace_waitpid.c new file mode 100644 index 000000000000..78b341c111f3 --- /dev/null +++ b/kernel/arch/amd64/t_ptrace_waitpid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAITPID +#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait.c b/kernel/arch/i386/t_ptrace_wait.c new file mode 100644 index 000000000000..698573bb0985 --- /dev/null +++ b/kernel/arch/i386/t_ptrace_wait.c @@ -0,0 +1,141 @@ +/* $NetBSD: t_ptrace_wait.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "h_macros.h" + +#include "../../t_ptrace_wait.h" + + +#if defined(HAVE_GPREGS) +ATF_TC(regs1); +ATF_TC_HEAD(regs1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_GETREGS and iterate over General Purpose registers"); +} + +ATF_TC_BODY(regs1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct reg r; + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + printf("Call GETREGS for the child process\n"); + ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); + + printf("EAX=%#" PRIxREGISTER "\n", r.r_eax); + printf("EBX=%#" PRIxREGISTER "\n", r.r_ebx); + printf("ECX=%#" PRIxREGISTER "\n", r.r_ecx); + printf("EDX=%#" PRIxREGISTER "\n", r.r_edx); + + printf("ESP=%#" PRIxREGISTER "\n", r.r_esp); + printf("EBP=%#" PRIxREGISTER "\n", r.r_ebp); + + printf("ESI=%#" PRIxREGISTER "\n", r.r_esi); + printf("EDI=%#" PRIxREGISTER "\n", r.r_edi); + + printf("EIP=%#" PRIxREGISTER "\n", r.r_eip); + + printf("EFLAGS=%#" PRIxREGISTER "\n", r.r_eflags); + + printf("CS=%#" PRIxREGISTER "\n", r.r_cs); + printf("SS=%#" PRIxREGISTER "\n", r.r_ss); + printf("DS=%#" PRIxREGISTER "\n", r.r_ds); + printf("ES=%#" PRIxREGISTER "\n", r.r_es); + printf("FS=%#" PRIxREGISTER "\n", r.r_fs); + printf("GS=%#" PRIxREGISTER "\n", r.r_gs); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +ATF_TP_ADD_TCS(tp) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1); + + return atf_no_error(); +} diff --git a/kernel/arch/i386/t_ptrace_wait3.c b/kernel/arch/i386/t_ptrace_wait3.c new file mode 100644 index 000000000000..56dda8e6978d --- /dev/null +++ b/kernel/arch/i386/t_ptrace_wait3.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT3 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait4.c b/kernel/arch/i386/t_ptrace_wait4.c new file mode 100644 index 000000000000..5fbdb53503b9 --- /dev/null +++ b/kernel/arch/i386/t_ptrace_wait4.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT4 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait6.c b/kernel/arch/i386/t_ptrace_wait6.c new file mode 100644 index 000000000000..117c844123c9 --- /dev/null +++ b/kernel/arch/i386/t_ptrace_wait6.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAIT6 +#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_waitid.c b/kernel/arch/i386/t_ptrace_waitid.c new file mode 100644 index 000000000000..274e8bd9f19c --- /dev/null +++ b/kernel/arch/i386/t_ptrace_waitid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAITID +#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_waitpid.c b/kernel/arch/i386/t_ptrace_waitpid.c new file mode 100644 index 000000000000..2fe5c570f9f7 --- /dev/null +++ b/kernel/arch/i386/t_ptrace_waitpid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + +#define TWAIT_WAITPID +#include "t_ptrace_wait.c" diff --git a/kernel/t_ptrace_wait.c b/kernel/t_ptrace_wait.c index d8935904c516..b1b4d1bef2c2 100644 --- a/kernel/t_ptrace_wait.c +++ b/kernel/t_ptrace_wait.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.57 2017/01/13 23:22:12 kamil Exp $ */ +/* $NetBSD: t_ptrace_wait.c,v 1.58 2017/01/14 04:37:55 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.57 2017/01/13 23:22:12 kamil Exp $"); +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.58 2017/01/14 04:37:55 kamil Exp $"); #include #include @@ -1072,6 +1072,118 @@ ATF_TC_BODY(eventmask2, tc) TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); } +ATF_TC(eventmask3); +ATF_TC_HEAD(eventmask3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that PTRACE_VFORK in EVENT_MASK is preserved"); +} + +ATF_TC_BODY(eventmask3, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_event_t set_event, get_event; + const int len = sizeof(ptrace_event_t); + + atf_tc_expect_fail("PR kern/51630"); + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + set_event.pe_set_event = PTRACE_VFORK; + ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); + ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); + ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +ATF_TC(eventmask4); +ATF_TC_HEAD(eventmask4, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that PTRACE_VFORK_DONE in EVENT_MASK is preserved"); +} + +ATF_TC_BODY(eventmask4, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_event_t set_event, get_event; + const int len = sizeof(ptrace_event_t); + + printf("Before forking process PID=%d\n", getpid()); + ATF_REQUIRE((child = fork()) != -1); + if (child == 0) { + printf("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + printf("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("Before exiting of the child process\n"); + _exit(exitval); + } + printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + set_event.pe_set_event = PTRACE_VFORK_DONE; + ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); + ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); + ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); + + printf("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + printf("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + #if defined(TWAIT_HAVE_PID) ATF_TC(fork1); ATF_TC_HEAD(fork1, tc) @@ -5193,6 +5305,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, eventmask1); ATF_TP_ADD_TC(tp, eventmask2); + ATF_TP_ADD_TC(tp, eventmask3); + ATF_TP_ADD_TC(tp, eventmask4); ATF_TP_ADD_TC_HAVE_PID(tp, fork1); ATF_TP_ADD_TC(tp, fork2); diff --git a/lib/libc/gen/exect/t_exect.c b/lib/libc/gen/exect/t_exect.c new file mode 100644 index 000000000000..4c85e4b2db4b --- /dev/null +++ b/lib/libc/gen/exect/t_exect.c @@ -0,0 +1,90 @@ +/* $NetBSD: t_exect.c,v 1.6 2016/12/12 10:34:55 joerg Exp $ */ + +/*- + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +ATF_TC(t_exect_null); + +ATF_TC_HEAD(t_exect_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Tests an empty exect(2) executing"); +} + +static volatile sig_atomic_t caught = 0; + +static void +sigtrap_handler(int sig, siginfo_t *info, void *ctx) +{ + ATF_REQUIRE_EQ(sig, SIGTRAP); + ATF_REQUIRE_EQ(info->si_code, TRAP_TRACE); + + ++caught; +} + +ATF_TC_BODY(t_exect_null, tc) +{ + struct sigaction act; + + /* + * Currently exect(3) is misdesigned -- see PR port-amd64/51700 and it + * needs to be redone from scratch. + * + * This test affects amd64 releng machines causing tests to hang or + * fail. As there is little point to test interface that is still not, + * designed and implemented and is breaking tests - skip it + * unconditionally for all ports. + */ + /* Prevent static analysis from requiring t_exec_null to be __dead. */ + if (!caught) + atf_tc_skip("exect(3) misdesigned and hangs - PR port-amd64/51700"); + + ATF_REQUIRE(sigemptyset(&act.sa_mask) == 0); + act.sa_sigaction = sigtrap_handler; + act.sa_flags = SA_SIGINFO; + + ATF_REQUIRE(sigaction(SIGTRAP, &act, 0) == 0); + + ATF_REQUIRE_ERRNO(EFAULT, exect(NULL, NULL, NULL) == -1); + + ATF_REQUIRE_EQ_MSG(caught, 1, "expected caught (1) != received (%d)", + (int)caught); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, t_exect_null); + + return atf_no_error(); +} diff --git a/lib/libpthread_dbg/h_common.h b/lib/libpthread_dbg/h_common.h new file mode 100644 index 000000000000..0a02b963d436 --- /dev/null +++ b/lib/libpthread_dbg/h_common.h @@ -0,0 +1,128 @@ +/* $NetBSD: h_common.h,v 1.2 2016/11/19 02:30:54 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + */ + + +#ifndef H_COMMON_H +#define H_COMMON_H + +#include +#include +#include +#include + +#include + +#define PTHREAD_REQUIRE(x) \ + do { \ + int ret = (x); \ + ATF_REQUIRE_MSG(ret == 0, "%s: %s", #x, strerror(ret)); \ + } while (0) + +#define PTHREAD_REQUIRE_STATUS(x, v) \ + do { \ + int ret = (x); \ + ATF_REQUIRE_MSG(ret == (v), "%s: %s", #x, strerror(ret)); \ + } while (0) + +static int __used +dummy_proc_read(void *arg, caddr_t addr, void *buf, size_t size) +{ + return TD_ERR_ERR; +} + +static int __used +dummy_proc_write(void *arg, caddr_t addr, void *buf, size_t size) +{ + return TD_ERR_ERR; +} + +static int __used +dummy_proc_lookup(void *arg, const char *sym, caddr_t *addr) +{ + return TD_ERR_ERR; +} + +static int __used +dummy_proc_regsize(void *arg, int regset, size_t *size) +{ + return TD_ERR_ERR; +} + +static int __used +dummy_proc_getregs(void *arg, int regset, int lwp, void *buf) +{ + return TD_ERR_ERR; +} + +static int __used +dummy_proc_setregs(void *arg, int regset, int lwp, void *buf) +{ + return TD_ERR_ERR; +} + +/* Minimalistic basic implementation */ + +static int __used +basic_proc_read(void *arg, caddr_t addr, void *buf, size_t size) +{ + memcpy(buf, addr, size); + + return TD_ERR_OK; +} + +static int __used +basic_proc_write(void *arg, caddr_t addr, void *buf, size_t size) +{ + memcpy(addr, buf, size); + + return TD_ERR_OK; +} + +static int __used +basic_proc_lookup(void *arg, const char *sym, caddr_t *addr) +{ + void *handle; + void *symbol; + + ATF_REQUIRE_MSG((handle = dlopen(NULL, RTLD_LOCAL | RTLD_LAZY)) + != NULL, "dlopen(3) failed: %s", dlerror()); + + symbol = dlsym(handle, sym); + + ATF_REQUIRE_MSG(dlclose(handle) == 0, "dlclose(3) failed: %s", + dlerror()); + + if (!symbol) + return TD_ERR_NOSYM; + + *addr = (caddr_t)(uintptr_t)symbol; + + return TD_ERR_OK; +} + +#endif // H_COMMON_H diff --git a/lib/libpthread_dbg/t_dummy.c b/lib/libpthread_dbg/t_dummy.c new file mode 100644 index 000000000000..367443cd0351 --- /dev/null +++ b/lib/libpthread_dbg/t_dummy.c @@ -0,0 +1,131 @@ +/* $NetBSD: t_dummy.c,v 1.6 2016/11/19 15:13:46 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +__RCSID("$NetBSD: t_dummy.c,v 1.6 2016/11/19 15:13:46 kamil Exp $"); + +#include "h_common.h" +#include +#include + +#include + + +ATF_TC(dummy1); +ATF_TC_HEAD(dummy1, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that dummy lookup functions stop td_open() with failure"); +} + +ATF_TC_BODY(dummy1, tc) +{ + + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + + dummy_callbacks.proc_read = dummy_proc_read; + dummy_callbacks.proc_write = dummy_proc_write; + dummy_callbacks.proc_lookup = dummy_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_ERR); +} + +ATF_TC(dummy2); +ATF_TC_HEAD(dummy2, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that td_open() for basic proc_{read,write,lookup} works"); +} + +ATF_TC_BODY(dummy2, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); +} + +ATF_TC(dummy3); +ATF_TC_HEAD(dummy3, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that calling twice td_open() for the same process fails"); +} + +ATF_TC_BODY(dummy3, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta1; + td_proc_t *main_ta2; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + printf("Calling td_open(3) for the first time - expecting success\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta1) == TD_ERR_OK); + + printf("Calling td_open(3) for the first time - expecting in-use\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta2) == + TD_ERR_INUSE); + + printf("Calling td_close(3) for the first successful call\n"); + ATF_REQUIRE(td_close(main_ta1) == TD_ERR_OK); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, dummy1); + ATF_TP_ADD_TC(tp, dummy2); + ATF_TP_ADD_TC(tp, dummy3); + + return atf_no_error(); +} diff --git a/lib/libpthread_dbg/t_threads.c b/lib/libpthread_dbg/t_threads.c new file mode 100644 index 000000000000..f22bbc9c0563 --- /dev/null +++ b/lib/libpthread_dbg/t_threads.c @@ -0,0 +1,719 @@ +/* $NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +__RCSID("$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $"); + +#include +#include +#include +#include +#include +#include + +#include + +#include "h_common.h" + +#define MAX_THREADS (size_t)10 + +ATF_TC(threads1); +ATF_TC_HEAD(threads1, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that td_thr_iter() call without extra logic works"); +} + +static volatile int exiting1; + +static void * +busyFunction1(void *arg) +{ + + while (exiting1 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads1(td_thread_t *thread, void *arg) +{ + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads1, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction1, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads1, NULL) == TD_ERR_OK); + + exiting1 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); +} + +ATF_TC(threads2); +ATF_TC_HEAD(threads2, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that td_thr_iter() call is executed for each thread once"); +} + +static volatile int exiting2; + +static void * +busyFunction2(void *arg) +{ + + while (exiting2 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads2(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads2, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction2, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads2, &count) == TD_ERR_OK); + + exiting2 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads3); +ATF_TC_HEAD(threads3, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that for each td_thr_iter() call td_thr_info() is valid"); +} + +static volatile int exiting3; + +static void * +busyFunction3(void *arg) +{ + + while (exiting3 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads3(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + td_thread_info_t info; + + ATF_REQUIRE(td_thr_info(thread, &info) == TD_ERR_OK); + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads3, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction3, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads3, &count) == TD_ERR_OK); + + exiting3 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads4); +ATF_TC_HEAD(threads4, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that for each td_thr_iter() call td_thr_getname() is " + "valid"); +} + +static volatile int exiting4; + +static void * +busyFunction4(void *arg) +{ + + while (exiting4 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads4(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + char name[PTHREAD_MAX_NAMELEN_NP]; + + ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK); + + printf("Thread name: %s\n", name); + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads4, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction4, NULL)); + } + + for (i = 0; i < MAX_THREADS; i++) { + PTHREAD_REQUIRE + (pthread_setname_np(threads[i], "test_%d", (void*)i)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads4, &count) == TD_ERR_OK); + + exiting4 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads5); +ATF_TC_HEAD(threads5, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that td_thr_getname() handles shorter buffer parameter " + "and the result is properly truncated"); +} + +static volatile int exiting5; + +static void * +busyFunction5(void *arg) +{ + + while (exiting5 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads5(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + /* Arbitrarily short string buffer */ + char name[3]; + + ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK); + + printf("Thread name: %s\n", name); + + /* strlen(3) does not count including a '\0' character */ + ATF_REQUIRE(strlen(name) < sizeof(name)); + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads5, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction5, NULL)); + } + + for (i = 0; i < MAX_THREADS; i++) { + PTHREAD_REQUIRE + (pthread_setname_np(threads[i], "test_%d", (void*)i)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads5, &count) == TD_ERR_OK); + + exiting5 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads6); +ATF_TC_HEAD(threads6, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that pthread_t can be translated with td_map_pth2thr() " + "to td_thread_t -- and assert earlier that td_thr_iter() call is " + "valid"); +} + +static volatile int exiting6; + +static void * +busyFunction6(void *arg) +{ + + while (exiting6 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads6(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads6, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction6, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads6, &count) == TD_ERR_OK); + + for (i = 0; i < MAX_THREADS; i++) { + td_thread_t *td_thread; + ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) + == TD_ERR_OK); + } + + exiting6 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads7); +ATF_TC_HEAD(threads7, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that pthread_t can be translated with td_map_pth2thr() " + "to td_thread_t -- and assert later that td_thr_iter() call is " + "valid"); +} + +static volatile int exiting7; + +static void * +busyFunction7(void *arg) +{ + + while (exiting7 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads7(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads7, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction7, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + for (i = 0; i < MAX_THREADS; i++) { + td_thread_t *td_thread; + ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) + == TD_ERR_OK); + } + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads7, &count) == TD_ERR_OK); + + exiting7 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads8); +ATF_TC_HEAD(threads8, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that pthread_t can be translated with td_map_pth2thr() " + "to td_thread_t -- compare thread's name of pthread_t and " + "td_thread_t"); +} + +static volatile int exiting8; + +static void * +busyFunction8(void *arg) +{ + + while (exiting8 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads8(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads8, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction8, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads8, &count) == TD_ERR_OK); + + for (i = 0; i < MAX_THREADS; i++) { + td_thread_t *td_thread; + char td_threadname[PTHREAD_MAX_NAMELEN_NP]; + char pth_threadname[PTHREAD_MAX_NAMELEN_NP]; + ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) + == TD_ERR_OK); + ATF_REQUIRE(td_thr_getname(td_thread, td_threadname, + sizeof(td_threadname)) == TD_ERR_OK); + PTHREAD_REQUIRE(pthread_getname_np(threads[i], pth_threadname, + sizeof(pth_threadname))); + ATF_REQUIRE(strcmp(td_threadname, pth_threadname) == 0); + } + + exiting8 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TC(threads9); +ATF_TC_HEAD(threads9, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Asserts that pthread_t can be translated with td_map_pth2thr() " + "to td_thread_t -- assert that thread is in the TD_STATE_RUNNING " + "state"); +} + +static volatile int exiting9; + +static void * +busyFunction9(void *arg) +{ + + while (exiting9 == 0) + usleep(50000); + + return NULL; +} + +static int +iterateThreads9(td_thread_t *thread, void *arg) +{ + int *counter = (int *)arg; + + ++(*counter); + + return TD_ERR_OK; +} + +ATF_TC_BODY(threads9, tc) +{ + struct td_proc_callbacks_t dummy_callbacks; + td_proc_t *main_ta; + size_t i; + pthread_t threads[MAX_THREADS]; + int count = 0; + + dummy_callbacks.proc_read = basic_proc_read; + dummy_callbacks.proc_write = basic_proc_write; + dummy_callbacks.proc_lookup = basic_proc_lookup; + dummy_callbacks.proc_regsize = dummy_proc_regsize; + dummy_callbacks.proc_getregs = dummy_proc_getregs; + dummy_callbacks.proc_setregs = dummy_proc_setregs; + + for (i = 0; i < MAX_THREADS; i++) { + printf("Creating thread %zu\n", i); + PTHREAD_REQUIRE + (pthread_create(&threads[i], NULL, busyFunction9, NULL)); + } + + printf("Calling td_open(3)\n"); + ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); + + for (i = 0; i < MAX_THREADS; i++) { + td_thread_t *td_thread; + td_thread_info_t info; + ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) + == TD_ERR_OK); + ATF_REQUIRE(td_thr_info(td_thread, &info) == TD_ERR_OK); + ATF_REQUIRE_EQ(info.thread_state, TD_STATE_RUNNING); + } + + ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads9, &count) == TD_ERR_OK); + + exiting9 = 1; + + printf("Calling td_close(3)\n"); + ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); + + ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, + "counted threads (%d) != expected threads (%zu)", + count, MAX_THREADS + 1); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, threads1); + ATF_TP_ADD_TC(tp, threads2); + ATF_TP_ADD_TC(tp, threads3); + ATF_TP_ADD_TC(tp, threads4); + ATF_TP_ADD_TC(tp, threads5); + ATF_TP_ADD_TC(tp, threads6); + ATF_TP_ADD_TC(tp, threads7); + ATF_TP_ADD_TC(tp, threads8); + ATF_TP_ADD_TC(tp, threads9); + + return atf_no_error(); +} diff --git a/lib/librefuse/t_refuse_opt.c b/lib/librefuse/t_refuse_opt.c new file mode 100644 index 000000000000..23c2803f8552 --- /dev/null +++ b/lib/librefuse/t_refuse_opt.c @@ -0,0 +1,418 @@ +/* $NetBSD: t_refuse_opt.c,v 1.8 2017/01/13 21:30:41 christos Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: t_refuse_opt.c,v 1.8 2017/01/13 21:30:41 christos Exp $"); + +#define _KERNTYPES +#include + +#include + +#include + +#include "h_macros.h" + +ATF_TC(t_fuse_opt_add_arg); +ATF_TC_HEAD(t_fuse_opt_add_arg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_add_arg(3) works"); +} + +ATF_TC_BODY(t_fuse_opt_add_arg, tc) +{ + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + RZ(fuse_opt_add_arg(&args, "foo")); + RZ(fuse_opt_add_arg(&args, "bar")); + + ATF_REQUIRE_EQ(args.argc, 2); + ATF_CHECK_STREQ(args.argv[0], "foo"); + ATF_CHECK_STREQ(args.argv[1], "bar"); + ATF_CHECK(args.allocated != 0); +} + +ATF_TC(t_fuse_opt_insert_arg); +ATF_TC_HEAD(t_fuse_opt_insert_arg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_insert_arg(3) works"); +} + +ATF_TC_BODY(t_fuse_opt_insert_arg, tc) +{ + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + RZ(fuse_opt_insert_arg(&args, 0, "foo")); + RZ(fuse_opt_insert_arg(&args, 0, "bar")); + + ATF_REQUIRE_EQ(args.argc, 2); + ATF_CHECK_STREQ(args.argv[0], "bar"); + ATF_CHECK_STREQ(args.argv[1], "foo"); + ATF_CHECK(args.allocated != 0); +} + +ATF_TC(t_fuse_opt_add_opt); +ATF_TC_HEAD(t_fuse_opt_add_opt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_add_opt(3) works"); +} + +ATF_TC_BODY(t_fuse_opt_add_opt, tc) +{ + char* opt = NULL; + + RZ(fuse_opt_add_opt(&opt, "fo\\o")); + ATF_CHECK_STREQ(opt, "fo\\o"); + + RZ(fuse_opt_add_opt(&opt, "ba,r")); + ATF_CHECK_STREQ(opt, "fo\\o,ba,r"); +} + +ATF_TC(t_fuse_opt_add_opt_escaped); +ATF_TC_HEAD(t_fuse_opt_add_opt_escaped, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_add_opt_escaped(3) works"); +} + +ATF_TC_BODY(t_fuse_opt_add_opt_escaped, tc) +{ + char* opt = NULL; + + RZ(fuse_opt_add_opt_escaped(&opt, "fo\\o")); + ATF_CHECK_STREQ(opt, "fo\\\\o"); + + RZ(fuse_opt_add_opt_escaped(&opt, "ba,r")); + ATF_CHECK_STREQ(opt, "fo\\\\o,ba\\,r"); +} + +ATF_TC(t_fuse_opt_match); +ATF_TC_HEAD(t_fuse_opt_match, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_match(3) works" + " for every form of templates"); +} + +ATF_TC_BODY(t_fuse_opt_match, tc) +{ + struct fuse_opt o1[] = { FUSE_OPT_KEY("-x" , 0), FUSE_OPT_END }; + struct fuse_opt o2[] = { FUSE_OPT_KEY("foo" , 0), FUSE_OPT_END }; + struct fuse_opt o3[] = { FUSE_OPT_KEY("foo=" , 0), FUSE_OPT_END }; + struct fuse_opt o4[] = { FUSE_OPT_KEY("foo=%s", 0), FUSE_OPT_END }; + struct fuse_opt o5[] = { FUSE_OPT_KEY("-x " , 0), FUSE_OPT_END }; + struct fuse_opt o6[] = { FUSE_OPT_KEY("-x %s" , 0), FUSE_OPT_END }; + + ATF_CHECK(fuse_opt_match(o1, "-x") == 1); + ATF_CHECK(fuse_opt_match(o1, "x") == 0); + + ATF_CHECK(fuse_opt_match(o2, "foo") == 1); + ATF_CHECK(fuse_opt_match(o2, "-foo") == 0); + + ATF_CHECK(fuse_opt_match(o3, "foo=bar") == 1); + ATF_CHECK(fuse_opt_match(o3, "foo" ) == 0); + + ATF_CHECK(fuse_opt_match(o4, "foo=bar") == 1); + ATF_CHECK(fuse_opt_match(o4, "foo" ) == 0); + + ATF_CHECK(fuse_opt_match(o5, "-xbar" ) == 1); + ATF_CHECK(fuse_opt_match(o5, "-x" ) == 1); + ATF_CHECK(fuse_opt_match(o5, "-x=bar") == 1); + ATF_CHECK(fuse_opt_match(o5, "bar" ) == 0); + + ATF_CHECK(fuse_opt_match(o6, "-xbar" ) == 1); + ATF_CHECK(fuse_opt_match(o6, "-x" ) == 1); + ATF_CHECK(fuse_opt_match(o6, "-x=bar") == 1); + ATF_CHECK(fuse_opt_match(o6, "bar" ) == 0); +} + +struct foofs_config { + int number; + char *string; + char* nonopt; +}; + +#define FOOFS_OPT(t, p, v) { t, offsetof(struct foofs_config, p), v } + +static struct fuse_opt foofs_opts[] = { + FOOFS_OPT("number=%i" , number, 0), + FOOFS_OPT("-n %i" , number, 0), + FOOFS_OPT("string=%s" , string, 0), + FOOFS_OPT("number1" , number, 1), + FOOFS_OPT("number2" , number, 2), + FOOFS_OPT("--number=three", number, 3), + FOOFS_OPT("--number=four" , number, 4), + FUSE_OPT_END +}; + +static int foo_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { + struct foofs_config *config = data; + + if (key == FUSE_OPT_KEY_NONOPT && config->nonopt == NULL) { + config->nonopt = strdup(arg); + return 0; + } + else { + return 1; + } +} + +ATF_TC(t_fuse_opt_parse_null_args); +ATF_TC_HEAD(t_fuse_opt_parse_null_args, tc) +{ + atf_tc_set_md_var(tc, "descr", "NULL args means an empty arguments vector"); +} + +ATF_TC_BODY(t_fuse_opt_parse_null_args, tc) +{ + struct foofs_config config; + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(NULL, &config, NULL, NULL) == 0); + ATF_CHECK_EQ(config.number, 0); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); +} + +ATF_TC(t_fuse_opt_parse_null_opts); +ATF_TC_HEAD(t_fuse_opt_parse_null_opts, tc) +{ + atf_tc_set_md_var(tc, "descr", "NULL opts means an opts array which only has FUSE_OPT_END"); +} + +ATF_TC_BODY(t_fuse_opt_parse_null_opts, tc) +{ + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + struct foofs_config config; + + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "number=1,string=foo")); + RZ(fuse_opt_add_arg(&args, "bar")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, NULL, NULL) == 0); + ATF_CHECK_EQ(config.number, 0); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 4); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + ATF_CHECK_STREQ(args.argv[1], "-o"); + ATF_CHECK_STREQ(args.argv[2], "number=1,string=foo"); + ATF_CHECK_STREQ(args.argv[3], "bar"); +} + +ATF_TC(t_fuse_opt_parse_null_proc); +ATF_TC_HEAD(t_fuse_opt_parse_null_proc, tc) +{ + atf_tc_set_md_var(tc, "descr", "NULL proc means a processor function always returning 1," + " i.e. keep the argument"); +} + +ATF_TC_BODY(t_fuse_opt_parse_null_proc, tc) +{ + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + struct foofs_config config; + + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "number=1,string=foo")); + RZ(fuse_opt_add_arg(&args, "bar")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, NULL) == 0); + ATF_CHECK_EQ(config.number, 1); + ATF_CHECK_STREQ(config.string, "foo"); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 2); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + ATF_CHECK_STREQ(args.argv[1], "bar"); +} + +ATF_TC(t_fuse_opt_parse); +ATF_TC_HEAD(t_fuse_opt_parse, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_parse(3) fully works"); +} + +ATF_TC_BODY(t_fuse_opt_parse, tc) +{ + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + struct foofs_config config; + + /* Standard form */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "number=1,string=foo")); + RZ(fuse_opt_add_arg(&args, "bar")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 1); + ATF_CHECK_STREQ(config.string, "foo"); + ATF_CHECK_STREQ(config.nonopt, "bar"); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + + /* Concatenated -o */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-onumber=1,unknown,string=foo")); + RZ(fuse_opt_add_arg(&args, "bar")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 1); + ATF_CHECK_STREQ(config.string, "foo"); + ATF_CHECK_STREQ(config.nonopt, "bar"); + ATF_CHECK_EQ(args.argc, 3); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + ATF_CHECK_STREQ(args.argv[1], "-o"); + ATF_CHECK_STREQ(args.argv[2], "unknown"); + + /* Sparse -o */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "bar")); + RZ(fuse_opt_add_arg(&args, "baz")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "number=1")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "unknown")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "string=foo")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 1); + ATF_CHECK_STREQ(config.string, "foo"); + ATF_CHECK_STREQ(config.nonopt, "bar"); + ATF_CHECK_EQ(args.argc, 4); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + ATF_CHECK_STREQ(args.argv[1], "-o"); + ATF_CHECK_STREQ(args.argv[2], "unknown"); + ATF_CHECK_STREQ(args.argv[3], "baz"); + + /* Separate -n %i */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-n")); + RZ(fuse_opt_add_arg(&args, "3")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 3); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + + /* Concatenated -n %i */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-n3")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 3); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + + /* -o constant */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "-o")); + RZ(fuse_opt_add_arg(&args, "number2")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 2); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + + /* -x constant */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "--number=four")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 4); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_EQ(config.nonopt, NULL); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + + /* end-of-options "--" marker */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "--")); + RZ(fuse_opt_add_arg(&args, "-onumber=1")); + RZ(fuse_opt_add_arg(&args, "-ostring=foo")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 0); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_STREQ(config.nonopt, "-onumber=1"); + ATF_CHECK_EQ(args.argc, 3); + ATF_CHECK_STREQ(args.argv[0], "foofs"); + ATF_CHECK_STREQ(args.argv[1], "--"); + ATF_CHECK_STREQ(args.argv[2], "-ostring=foo"); + + /* The "--" marker at the last of outargs should be removed */ + fuse_opt_free_args(&args); + RZ(fuse_opt_add_arg(&args, "foofs")); + RZ(fuse_opt_add_arg(&args, "--")); + RZ(fuse_opt_add_arg(&args, "-onumber=1")); + + memset(&config, 0, sizeof(config)); + ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0); + ATF_CHECK_EQ(config.number, 0); + ATF_CHECK_EQ(config.string, NULL); + ATF_CHECK_STREQ(config.nonopt, "-onumber=1"); + ATF_CHECK_EQ(args.argc, 1); + ATF_CHECK_STREQ(args.argv[0], "foofs"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, t_fuse_opt_add_arg); + ATF_TP_ADD_TC(tp, t_fuse_opt_insert_arg); + ATF_TP_ADD_TC(tp, t_fuse_opt_add_opt); + ATF_TP_ADD_TC(tp, t_fuse_opt_add_opt_escaped); + ATF_TP_ADD_TC(tp, t_fuse_opt_match); + ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_args); + ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_opts); + ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_proc); + ATF_TP_ADD_TC(tp, t_fuse_opt_parse); + + return atf_no_error(); +} diff --git a/net/if_tun/t_tun.sh b/net/if_tun/t_tun.sh new file mode 100755 index 000000000000..87df39ab5886 --- /dev/null +++ b/net/if_tun/t_tun.sh @@ -0,0 +1,138 @@ +# $NetBSD: t_tun.sh,v 1.4 2016/11/07 05:25:37 ozaki-r Exp $ +# +# Copyright (c) 2016 Internet Initiative Japan Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE 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. +# + +RUMP_FLAGS="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6" +RUMP_FLAGS="$RUMP_FLAGS -lrumpnet_shmif -lrumpnet_tun -lrumpdev" + +BUS=bus +SOCK_LOCAL=unix://commsock1 +SOCK_REMOTE=unix://commsock2 +IP_LOCAL=10.0.0.1 +IP_REMOTE=10.0.0.2 + +DEBUG=${DEBUG:-true} + +atf_test_case tun_create_destroy cleanup +tun_create_destroy_head() +{ + + atf_set "descr" "tests of creation and deletion of tun interface" + atf_set "require.progs" "rump_server" +} + +tun_create_destroy_body() +{ + + atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${SOCK_LOCAL} + + export RUMP_SERVER=${SOCK_LOCAL} + + atf_check -s exit:0 rump.ifconfig tun0 create + atf_check -s exit:0 rump.ifconfig tun0 up + atf_check -s exit:0 rump.ifconfig tun0 down + atf_check -s exit:0 rump.ifconfig tun0 destroy +} + +tun_create_destroy_cleanup() +{ + + RUMP_SERVER=${SOCK_LOCAL} rump.halt +} + +atf_test_case tun_setup cleanup +tun_setup_head() +{ + + atf_set "descr" "tests of setting up a tunnel" + atf_set "require.progs" "rump_server" +} + +check_route_entry() +{ + local ip=$(echo $1 |sed 's/\./\\./g') + local gw=$2 + local flags=$3 + local iface=$4 + + atf_check -s exit:0 -o match:" $flags " -e ignore -x \ + "rump.netstat -rn -f inet | grep ^'$ip'" + atf_check -s exit:0 -o match:" $gw " -e ignore -x \ + "rump.netstat -rn -f inet | grep ^'$ip'" + atf_check -s exit:0 -o match:" $iface" -e ignore -x \ + "rump.netstat -rn -f inet | grep ^'$ip'" +} + +tun_setup_body() +{ + + atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${SOCK_LOCAL} + atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${SOCK_REMOTE} + + export RUMP_SERVER=${SOCK_LOCAL} + + atf_check -s exit:0 rump.ifconfig shmif0 create + atf_check -s exit:0 rump.ifconfig shmif0 linkstr $BUS + atf_check -s exit:0 rump.ifconfig shmif0 ${IP_LOCAL}/24 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_REMOTE} + + atf_check -s exit:0 rump.ifconfig shmif0 create + atf_check -s exit:0 rump.ifconfig shmif0 linkstr $BUS + atf_check -s exit:0 rump.ifconfig shmif0 ${IP_REMOTE}/24 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 rump.ifconfig tun0 create + atf_check -s exit:0 rump.ifconfig tun0 ${IP_LOCAL} ${IP_REMOTE} up + atf_check -s exit:0 \ + -o match:"inet ${IP_LOCAL}/32 -> ${IP_REMOTE}" rump.ifconfig tun0 + $DEBUG && rump.netstat -nr -f inet + check_route_entry ${IP_REMOTE} ${IP_LOCAL} UH tun0 + + export RUMP_SERVER=${SOCK_REMOTE} + atf_check -s exit:0 rump.ifconfig tun0 create + atf_check -s exit:0 rump.ifconfig tun0 ${IP_REMOTE} ${IP_LOCAL} up + atf_check -s exit:0 \ + -o match:"inet ${IP_REMOTE}/32 -> ${IP_LOCAL}" rump.ifconfig tun0 + $DEBUG && rump.netstat -nr -f inet + check_route_entry ${IP_LOCAL} ${IP_REMOTE} UH tun0 +} + +tun_setup_cleanup() +{ + + RUMP_SERVER=${SOCK_LOCAL} rump.halt + RUMP_SERVER=${SOCK_REMOTE} rump.halt +} + +atf_init_test_cases() +{ + + atf_add_test_case tun_create_destroy + atf_add_test_case tun_setup +} diff --git a/net/if_vlan/t_vlan.sh b/net/if_vlan/t_vlan.sh new file mode 100755 index 000000000000..a6902fb1e247 --- /dev/null +++ b/net/if_vlan/t_vlan.sh @@ -0,0 +1,115 @@ +# $NetBSD: t_vlan.sh,v 1.1 2016/11/26 03:19:49 ozaki-r Exp $ +# +# Copyright (c) 2016 Internet Initiative Japan Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE 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. +# + +BUS=bus +SOCK_LOCAL=unix://commsock1 +SOCK_REMOTE=unix://commsock2 +IP_LOCAL=10.0.0.1 +IP_REMOTE=10.0.0.2 + +DEBUG=${DEBUG:-false} + +atf_test_case vlan_create_destroy cleanup +vlan_create_destroy_head() +{ + + atf_set "descr" "tests of creation and deletion of vlan interface" + atf_set "require.progs" "rump_server" +} + +vlan_create_destroy_body() +{ + + rump_server_start $SOCK_LOCAL vlan + + export RUMP_SERVER=${SOCK_LOCAL} + + atf_check -s exit:0 rump.ifconfig vlan0 create + atf_check -s exit:0 rump.ifconfig vlan0 up + atf_check -s exit:0 rump.ifconfig vlan0 down + atf_check -s exit:0 rump.ifconfig vlan0 destroy +} + +vlan_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_basic cleanup +vlan_basic_head() +{ + + atf_set "descr" "tests of communications over vlan interfaces" + atf_set "require.progs" "rump_server" +} + +vlan_basic_body() +{ + + rump_server_start $SOCK_LOCAL vlan + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_start $SOCK_REMOTE vlan + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 up + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 up + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig vlan0 create + atf_check -s exit:0 rump.ifconfig vlan0 vlan 10 vlanif shmif0 + atf_check -s exit:0 rump.ifconfig vlan0 $IP_LOCAL/24 + atf_check -s exit:0 rump.ifconfig vlan0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig vlan0 create + atf_check -s exit:0 rump.ifconfig vlan0 vlan 10 vlanif shmif0 + atf_check -s exit:0 rump.ifconfig vlan0 $IP_REMOTE/24 + atf_check -s exit:0 rump.ifconfig vlan0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP_REMOTE +} + +vlan_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case vlan_create_destroy + atf_add_test_case vlan_basic +} diff --git a/sys/uvm/t_uvm_physseg.c b/sys/uvm/t_uvm_physseg.c new file mode 100644 index 000000000000..a5f2fa9e9067 --- /dev/null +++ b/sys/uvm/t_uvm_physseg.c @@ -0,0 +1,2377 @@ +/* $NetBSD: t_uvm_physseg.c,v 1.2 2016/12/22 08:15:20 cherry Exp $ */ + +/*- + * Copyright (c) 2015, 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Santhosh N. Raju and + * by Cherry G. Mathew + * + * 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. + */ + +#include +__RCSID("$NetBSD: t_uvm_physseg.c,v 1.2 2016/12/22 08:15:20 cherry Exp $"); + +/* + * If this line is commented out tests related to uvm_physseg_get_pmseg() + * wont run. + * + * Have a look at machine/uvm_physseg.h for more details. + */ +#define __HAVE_PMAP_PHYSSEG + +/* + * This is a dummy struct used for testing purposes + * + * In reality this struct would exist in the MD part of the code residing in + * machines/vmparam.h + */ + +#ifdef __HAVE_PMAP_PHYSSEG +struct pmap_physseg { + int dummy_variable; /* Dummy variable use for testing */ +}; +#endif + +/* Testing API - assumes userland */ +/* Provide Kernel API equivalents */ +#include +#include +#include +#include /* memset(3) et. al */ +#include /* printf(3) */ +#include /* malloc(3) */ +#include +#include + +#define PRIxPADDR "lx" +#define PRIxPSIZE "lx" +#define PRIuPSIZE "lu" +#define PRIxVADDR "lx" +#define PRIxVSIZE "lx" +#define PRIuVSIZE "lu" + +#define UVM_HOTPLUG /* Enable hotplug with rbtree. */ +#define PMAP_STEAL_MEMORY +#define DEBUG /* Enable debug functionality. */ + +typedef unsigned long vaddr_t; +typedef unsigned long paddr_t; +typedef unsigned long psize_t; +typedef unsigned long vsize_t; + +#include +#include + +#ifndef DIAGNOSTIC +#define KASSERTMSG(e, msg, ...) /* NOTHING */ +#define KASSERT(e) /* NOTHING */ +#else +#define KASSERT(a) assert(a) +#define KASSERTMSG(exp, ...) printf(__VA_ARGS__); assert((exp)) +#endif + +#define VM_PHYSSEG_STRAT VM_PSTRAT_BSEARCH + +#define VM_NFREELIST 4 +#define VM_FREELIST_DEFAULT 0 +#define VM_FREELIST_FIRST16 3 +#define VM_FREELIST_FIRST1G 2 +#define VM_FREELIST_FIRST4G 1 + +/* + * Used in tests when Array implementation is tested + */ +#if !defined(VM_PHYSSEG_MAX) +#define VM_PHYSSEG_MAX 1 +#endif + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) +#define atop(x) (((paddr_t)(x)) >> PAGE_SHIFT) +#define ptoa(x) (((paddr_t)(x)) << PAGE_SHIFT) + +#define mutex_enter(l) +#define mutex_exit(l) + +psize_t physmem; + +struct uvmexp uvmexp; /* decl */ + +/* + * uvm structure borrowed from uvm.h + * + * Remember this is a dummy structure used within the ATF Tests and + * uses only necessary fields from the original uvm struct. + * See uvm/uvm.h for the full struct. + */ + +struct uvm { + /* vm_page related parameters */ + + bool page_init_done; /* TRUE if uvm_page_init() finished */ +} uvm; + +#include + +void * +kmem_alloc(size_t size, km_flag_t flags) +{ + return malloc(size); +} + +void * +kmem_zalloc(size_t size, km_flag_t flags) +{ + void *ptr; + ptr = malloc(size); + + memset(ptr, 0, size); + + return ptr; +} + +void +kmem_free(void *mem, size_t size) +{ + free(mem); +} + +static void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + KASSERT(false); + + /*NOTREACHED*/ +} + +static void +uvm_pagefree(struct vm_page *pg) +{ + return; +} + +#if defined(UVM_HOTPLUG) +static void +uvmpdpol_reinit(void) +{ + return; +} +#endif /* UVM_HOTPLUG */ + +/* end - Provide Kernel API equivalents */ + + +#include "uvm/uvm_physseg.c" + +#include + +#define SIXTYFOUR_KILO (64 * 1024) +#define ONETWENTYEIGHT_KILO (128 * 1024) +#define TWOFIFTYSIX_KILO (256 * 1024) +#define FIVEONETWO_KILO (512 * 1024) +#define ONE_MEGABYTE (1024 * 1024) +#define TWO_MEGABYTE (2 * 1024 * 1024) + +/* Sample Page Frame Numbers */ +#define VALID_START_PFN_1 atop(0) +#define VALID_END_PFN_1 atop(ONE_MEGABYTE) +#define VALID_AVAIL_START_PFN_1 atop(0) +#define VALID_AVAIL_END_PFN_1 atop(ONE_MEGABYTE) + +#define VALID_START_PFN_2 atop(ONE_MEGABYTE + 1) +#define VALID_END_PFN_2 atop(ONE_MEGABYTE * 2) +#define VALID_AVAIL_START_PFN_2 atop(ONE_MEGABYTE + 1) +#define VALID_AVAIL_END_PFN_2 atop(ONE_MEGABYTE * 2) + +#define VALID_START_PFN_3 atop((ONE_MEGABYTE * 2) + 1) +#define VALID_END_PFN_3 atop(ONE_MEGABYTE * 3) +#define VALID_AVAIL_START_PFN_3 atop((ONE_MEGABYTE * 2) + 1) +#define VALID_AVAIL_END_PFN_3 atop(ONE_MEGABYTE * 3) + +#define VALID_START_PFN_4 atop((ONE_MEGABYTE * 3) + 1) +#define VALID_END_PFN_4 atop(ONE_MEGABYTE * 4) +#define VALID_AVAIL_START_PFN_4 atop((ONE_MEGABYTE * 3) + 1) +#define VALID_AVAIL_END_PFN_4 atop(ONE_MEGABYTE * 4) + +/* + * Total number of pages (of 4K size each) should be 256 for 1MB of memory. + */ +#define PAGE_COUNT_1M 256 + +/* + * A debug fucntion to print the content of upm. + */ + static inline void + uvm_physseg_dump_seg(uvm_physseg_t upm) + { +#if defined(DEBUG) + printf("%s: seg->start == %ld\n", __func__, + uvm_physseg_get_start(upm)); + printf("%s: seg->end == %ld\n", __func__, + uvm_physseg_get_end(upm)); + printf("%s: seg->avail_start == %ld\n", __func__, + uvm_physseg_get_avail_start(upm)); + printf("%s: seg->avail_end == %ld\n", __func__, + uvm_physseg_get_avail_end(upm)); + + printf("====\n\n"); +#else + return; +#endif /* DEBUG */ + } + +/* + * Private accessor that gets the value of uvm_physseg_graph.nentries + */ +static int +uvm_physseg_get_entries(void) +{ +#if defined(UVM_HOTPLUG) + return uvm_physseg_graph.nentries; +#else + return vm_nphysmem; +#endif /* UVM_HOTPLUG */ +} + +#if !defined(UVM_HOTPLUG) +static void * +uvm_physseg_alloc(size_t sz) +{ + return &vm_physmem[vm_nphysseg++]; +} +#endif + +/* + * Test Fixture SetUp(). + */ +static void +setup(void) +{ + /* Prerequisites for running certain calls in uvm_physseg */ + uvmexp.pagesize = PAGE_SIZE; + uvmexp.npages = 0; + uvm.page_init_done = false; + uvm_physseg_init(); +} + + +/* <---- Tests for Internal functions ----> */ +#if defined(UVM_HOTPLUG) +ATF_TC(uvm_physseg_alloc_atboot_mismatch); +ATF_TC_HEAD(uvm_physseg_alloc_atboot_mismatch, tc) +{ + atf_tc_set_md_var(tc, "descr", "boot time uvm_physseg_alloc() sanity" + "size mismatch alloc() test."); +} + +ATF_TC_BODY(uvm_physseg_alloc_atboot_mismatch, tc) +{ + uvm.page_init_done = false; + + atf_tc_expect_signal(SIGABRT, "size mismatch alloc()"); + + uvm_physseg_alloc(sizeof(struct uvm_physseg) - 1); +} + +ATF_TC(uvm_physseg_alloc_atboot_overrun); +ATF_TC_HEAD(uvm_physseg_alloc_atboot_overrun, tc) +{ + atf_tc_set_md_var(tc, "descr", "boot time uvm_physseg_alloc() sanity" + "array overrun alloc() test."); +} + +ATF_TC_BODY(uvm_physseg_alloc_atboot_overrun, tc) +{ + uvm.page_init_done = false; + + atf_tc_expect_signal(SIGABRT, "array overrun alloc()"); + + uvm_physseg_alloc((VM_PHYSSEG_MAX + 1) * sizeof(struct uvm_physseg)); + +} + +ATF_TC(uvm_physseg_alloc_sanity); +ATF_TC_HEAD(uvm_physseg_alloc_sanity, tc) +{ + atf_tc_set_md_var(tc, "descr", "further uvm_physseg_alloc() sanity checks"); +} + +ATF_TC_BODY(uvm_physseg_alloc_sanity, tc) +{ + + /* At boot time */ + uvm.page_init_done = false; + + /* Correct alloc */ + ATF_REQUIRE(uvm_physseg_alloc(VM_PHYSSEG_MAX * sizeof(struct uvm_physseg))); + + /* Retry static alloc()s as dynamic - we expect them to pass */ + uvm.page_init_done = true; + ATF_REQUIRE(uvm_physseg_alloc(sizeof(struct uvm_physseg) - 1)); + ATF_REQUIRE(uvm_physseg_alloc(2 * VM_PHYSSEG_MAX * sizeof(struct uvm_physseg))); +} + +ATF_TC(uvm_physseg_free_atboot_mismatch); +ATF_TC_HEAD(uvm_physseg_free_atboot_mismatch, tc) +{ + atf_tc_set_md_var(tc, "descr", "boot time uvm_physseg_free() sanity" + "size mismatch free() test."); +} + +ATF_TC_BODY(uvm_physseg_free_atboot_mismatch, tc) +{ + uvm.page_init_done = false; + + atf_tc_expect_signal(SIGABRT, "size mismatch free()"); + + uvm_physseg_free(&uvm_physseg[0], sizeof(struct uvm_physseg) - 1); +} + +ATF_TC(uvm_physseg_free_sanity); +ATF_TC_HEAD(uvm_physseg_free_sanity, tc) +{ + atf_tc_set_md_var(tc, "descr", "further uvm_physseg_free() sanity checks"); +} + +ATF_TC_BODY(uvm_physseg_free_sanity, tc) +{ + + /* At boot time */ + uvm.page_init_done = false; + + struct uvm_physseg *seg; + +#if VM_PHYSSEG_MAX > 1 + /* + * Note: free()ing the entire array is considered to be an + * error. Thus VM_PHYSSEG_MAX - 1. + */ + + seg = uvm_physseg_alloc((VM_PHYSSEG_MAX - 1) * sizeof(*seg)); + uvm_physseg_free(seg, (VM_PHYSSEG_MAX - 1) * sizeof(struct uvm_physseg)); +#endif + + /* Retry static alloc()s as dynamic - we expect them to pass */ + uvm.page_init_done = true; + + seg = uvm_physseg_alloc(sizeof(struct uvm_physseg) - 1); + uvm_physseg_free(seg, sizeof(struct uvm_physseg) - 1); + + seg = uvm_physseg_alloc(2 * VM_PHYSSEG_MAX * sizeof(struct uvm_physseg)); + + uvm_physseg_free(seg, 2 * VM_PHYSSEG_MAX * sizeof(struct uvm_physseg)); +} + +#if VM_PHYSSEG_MAX > 1 +ATF_TC(uvm_physseg_atboot_free_leak); +ATF_TC_HEAD(uvm_physseg_atboot_free_leak, tc) +{ + atf_tc_set_md_var(tc, "descr", + "does free() leak at boot ?\n" + "This test needs VM_PHYSSEG_MAX > 1)"); +} + +ATF_TC_BODY(uvm_physseg_atboot_free_leak, tc) +{ + + /* At boot time */ + uvm.page_init_done = false; + + /* alloc to array size */ + struct uvm_physseg *seg; + seg = uvm_physseg_alloc(VM_PHYSSEG_MAX * sizeof(*seg)); + + uvm_physseg_free(seg, sizeof(*seg)); + + atf_tc_expect_signal(SIGABRT, "array overrun on alloc() after leak"); + + ATF_REQUIRE(uvm_physseg_alloc(sizeof(struct uvm_physseg))); +} +#endif /* VM_PHYSSEG_MAX */ +#endif /* UVM_HOTPLUG */ + +/* + * Note: This function replicates verbatim what happens in + * uvm_page.c:uvm_page_init(). + * + * Please track any changes that happen there. + */ +static void +uvm_page_init_fake(struct vm_page *pagearray, psize_t pagecount) +{ + uvm_physseg_t bank; + size_t n; + + for (bank = uvm_physseg_get_first(), + uvm_physseg_seg_chomp_slab(bank, pagearray, pagecount); + uvm_physseg_valid_p(bank); + bank = uvm_physseg_get_next(bank)) { + + n = uvm_physseg_get_end(bank) - uvm_physseg_get_start(bank); + uvm_physseg_seg_alloc_from_slab(bank, n); + uvm_physseg_init_seg(bank, pagearray); + + /* set up page array pointers */ + pagearray += n; + pagecount -= n; + } + + uvm.page_init_done = true; +} + +ATF_TC(uvm_physseg_plug); +ATF_TC_HEAD(uvm_physseg_plug, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test plug functionality."); +} +/* Note: We only do the second boot time plug if VM_PHYSSEG_MAX > 1 */ +ATF_TC_BODY(uvm_physseg_plug, tc) +{ + int nentries = 0; /* Count of entries via plug done so far */ + uvm_physseg_t upm1; +#if VM_PHYSSEG_MAX > 2 + uvm_physseg_t upm2; +#endif + +#if VM_PHYSSEG_MAX > 1 + uvm_physseg_t upm3; +#endif + uvm_physseg_t upm4; + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + psize_t npages2 = (VALID_END_PFN_2 - VALID_START_PFN_2); + psize_t npages3 = (VALID_END_PFN_3 - VALID_START_PFN_3); + psize_t npages4 = (VALID_END_PFN_4 - VALID_START_PFN_4); + struct vm_page *pgs, *slab = malloc(sizeof(struct vm_page) * (npages1 +#if VM_PHYSSEG_MAX > 2 + + npages2 +#endif + + npages3)); + + /* Fake early boot */ + + setup(); + + /* Vanilla plug x 2 */ + ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_1, npages1, &upm1), true); + ATF_REQUIRE_EQ(++nentries, uvm_physseg_get_entries()); + ATF_REQUIRE_EQ(0, uvmexp.npages); + +#if VM_PHYSSEG_MAX > 2 + ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_2, npages2, &upm2), true); + ATF_REQUIRE_EQ(++nentries, uvm_physseg_get_entries()); + ATF_REQUIRE_EQ(0, uvmexp.npages); +#endif + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2 + npages3); + + ATF_CHECK_EQ(npages1 +#if VM_PHYSSEG_MAX > 2 + + npages2 +#endif + , uvmexp.npages); +#if VM_PHYSSEG_MAX > 1 + /* Scavenge plug - goes into the same slab */ + ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_3, npages3, &upm3), true); + ATF_REQUIRE_EQ(++nentries, uvm_physseg_get_entries()); + ATF_REQUIRE_EQ(npages1 +#if VM_PHYSSEG_MAX > 2 + + npages2 +#endif + + npages3, uvmexp.npages); + + /* Scavenge plug should fit right in the slab */ + pgs = uvm_physseg_get_pg(upm3, 0); + ATF_REQUIRE(pgs > slab && pgs < (slab + npages1 + npages2 + npages3)); +#endif + /* Hot plug - goes into a brand new slab */ + ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_4, npages4, &upm4), true); + /* The hot plug slab should have nothing to do with the original slab */ + pgs = uvm_physseg_get_pg(upm4, 0); + ATF_REQUIRE(pgs < slab || pgs > (slab + npages1 +#if VM_PHYSSEG_MAX > 2 + + npages2 +#endif + + npages3)); + +} +ATF_TC(uvm_physseg_unplug); +ATF_TC_HEAD(uvm_physseg_unplug, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test unplug functionality."); +} +ATF_TC_BODY(uvm_physseg_unplug, tc) +{ + paddr_t pa = 0; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + psize_t npages2 = (VALID_END_PFN_2 - VALID_START_PFN_2); + psize_t npages3 = (VALID_END_PFN_3 - VALID_START_PFN_3); + + struct vm_page *slab = malloc(sizeof(struct vm_page) * (npages1 + npages2 + npages3)); + + uvm_physseg_t upm; + + /* Boot time */ + setup(); + + /* We start with zero segments */ + ATF_REQUIRE_EQ(true, uvm_physseg_plug(atop(0), atop(ONE_MEGABYTE), NULL)); + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + /* Do we have an arbitrary offset in there ? */ + uvm_physseg_find(atop(TWOFIFTYSIX_KILO), &pa); + ATF_REQUIRE_EQ(pa, atop(TWOFIFTYSIX_KILO)); + ATF_REQUIRE_EQ(0, uvmexp.npages); /* Boot time sanity */ + +#if VM_PHYSSEG_MAX == 1 + /* + * This is the curious case at boot time, of having one + * extent(9) static entry per segment, which means that a + * fragmenting unplug will fail. + */ + atf_tc_expect_signal(SIGABRT, "fragmenting unplug for single segment"); + + /* + * In order to test the fragmenting cases, please set + * VM_PHYSSEG_MAX > 1 + */ +#endif + /* Now let's unplug from the middle */ + ATF_REQUIRE_EQ(true, uvm_physseg_unplug(atop(TWOFIFTYSIX_KILO), atop(FIVEONETWO_KILO))); + /* verify that a gap exists at TWOFIFTYSIX_KILO */ + pa = 0; /* reset */ + uvm_physseg_find(atop(TWOFIFTYSIX_KILO), &pa); + ATF_REQUIRE_EQ(pa, 0); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2 + npages3); + /* Account for the unplug */ + ATF_CHECK_EQ(atop(FIVEONETWO_KILO), uvmexp.npages); + + /* Original entry should fragment into two */ + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + upm = uvm_physseg_find(atop(TWOFIFTYSIX_KILO + FIVEONETWO_KILO), NULL); + + ATF_REQUIRE(uvm_physseg_valid_p(upm)); + + /* Now unplug the tail fragment - should swallow the complete entry */ + ATF_REQUIRE_EQ(true, uvm_physseg_unplug(atop(TWOFIFTYSIX_KILO + FIVEONETWO_KILO), atop(TWOFIFTYSIX_KILO))); + + /* The "swallow" above should have invalidated the handle */ + ATF_REQUIRE_EQ(false, uvm_physseg_valid_p(upm)); + + /* Only the first one is left now */ + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Unplug from the back */ + ATF_REQUIRE_EQ(true, uvm_physseg_unplug(atop(ONETWENTYEIGHT_KILO), atop(ONETWENTYEIGHT_KILO))); + /* Shouldn't change the number of segments */ + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Unplug from the front */ + ATF_REQUIRE_EQ(true, uvm_physseg_unplug(0, atop(SIXTYFOUR_KILO))); + /* Shouldn't change the number of segments */ + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Unplugging the final fragment should fail */ + atf_tc_expect_signal(SIGABRT, "Unplugging the last segment"); + ATF_REQUIRE_EQ(true, uvm_physseg_unplug(atop(SIXTYFOUR_KILO), atop(SIXTYFOUR_KILO))); +} + + +/* <---- end Tests for Internal functions ----> */ + +/* Tests for functions exported via uvm_physseg.h */ +ATF_TC(uvm_physseg_init); +ATF_TC_HEAD(uvm_physseg_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_init() call\ + initializes the vm_physmem struct which holds the rb_tree."); +} +ATF_TC_BODY(uvm_physseg_init, tc) +{ + uvm_physseg_init(); + + ATF_REQUIRE_EQ(0, uvm_physseg_get_entries()); +} + +ATF_TC(uvm_page_physload_preload); +ATF_TC_HEAD(uvm_page_physload_preload, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physload() \ + call works without a panic() in a preload scenario."); +} +ATF_TC_BODY(uvm_page_physload_preload, tc) +{ + uvm_physseg_t upm; + + setup(); + + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Should return a valid handle */ + ATF_REQUIRE(uvm_physseg_valid_p(upm)); + + /* No pages should be allocated yet */ + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* After the first call one segment should exist */ + ATF_CHECK_EQ(1, uvm_physseg_get_entries()); + + /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + /* Should return a valid handle */ + ATF_REQUIRE(uvm_physseg_valid_p(upm)); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* After the second call two segments should exist */ + ATF_CHECK_EQ(2, uvm_physseg_get_entries()); +#endif +} + +ATF_TC(uvm_page_physload_postboot); +ATF_TC_HEAD(uvm_page_physload_postboot, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physload() \ + panic()s in a post boot scenario."); +} +ATF_TC_BODY(uvm_page_physload_postboot, tc) +{ + uvm_physseg_t upm; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + psize_t npages2 = (VALID_END_PFN_2 - VALID_START_PFN_2); + + struct vm_page *slab = malloc(sizeof(struct vm_page) * (npages1 + npages2)); + + setup(); + + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Should return a valid handle */ + ATF_REQUIRE(uvm_physseg_valid_p(upm)); + + /* No pages should be allocated yet */ + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* After the first call one segment should exist */ + ATF_CHECK_EQ(1, uvm_physseg_get_entries()); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2); + + atf_tc_expect_signal(SIGABRT, + "uvm_page_physload() called post boot"); + + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + /* Should return a valid handle */ + ATF_REQUIRE(uvm_physseg_valid_p(upm)); + + ATF_REQUIRE_EQ(npages1 + npages2, uvmexp.npages); + + /* After the second call two segments should exist */ + ATF_CHECK_EQ(2, uvm_physseg_get_entries()); +} + +ATF_TC(uvm_physseg_handle_immutable); +ATF_TC_HEAD(uvm_physseg_handle_immutable, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the uvm_physseg_t handle is \ + immutable."); +} +ATF_TC_BODY(uvm_physseg_handle_immutable, tc) +{ + uvm_physseg_t upm; + + /* We insert the segments in out of order */ + + setup(); + + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID_EMPTY, uvm_physseg_get_prev(upm)); + + /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ +#if VM_PHYSSEG_MAX > 1 + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + /* Fetch Previous, we inserted a lower value */ + upm = uvm_physseg_get_prev(upm); + +#if !defined(UVM_HOTPLUG) + /* + * This test is going to fail for the Array Implementation but is + * expected to pass in the RB Tree implementation. + */ + /* Failure can be expected iff there are more than one handles */ + atf_tc_expect_fail("Mutable handle in static array impl."); +#endif + ATF_CHECK(UVM_PHYSSEG_TYPE_INVALID_EMPTY != upm); + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_seg_chomp_slab); +ATF_TC_HEAD(uvm_physseg_seg_chomp_slab, tc) +{ + atf_tc_set_md_var(tc, "descr", "The slab import code.()"); + +} +ATF_TC_BODY(uvm_physseg_seg_chomp_slab, tc) +{ + int err; + size_t i; + struct uvm_physseg *seg; + struct vm_page *slab, *pgs; + const size_t npages = UVM_PHYSSEG_BOOT_UNPLUG_MAX; /* Number of pages */ + + setup(); + + /* This is boot time */ + slab = malloc(sizeof(struct vm_page) * npages * 2); + + seg = uvm_physseg_alloc(sizeof(struct uvm_physseg)); + + uvm_physseg_seg_chomp_slab(PHYSSEG_NODE_TO_HANDLE(seg), slab, npages * 2); + + /* Should be able to allocate two 128 * sizeof(*slab) */ + ATF_REQUIRE_EQ(0, extent_alloc(seg->ext, sizeof(*slab), 1, 0, EX_BOUNDZERO, (void *)&pgs)); + err = extent_free(seg->ext, (u_long) pgs, sizeof(*slab), EX_BOUNDZERO); + +#if VM_PHYSSEG_MAX == 1 + /* + * free() needs an extra region descriptor, but we only have + * one! The classic alloc() at free() problem + */ + + ATF_REQUIRE_EQ(ENOMEM, err); +#else + /* Try alloc/free at static time */ + for (i = 0; i < npages; i++) { + ATF_REQUIRE_EQ(0, extent_alloc(seg->ext, sizeof(*slab), 1, 0, EX_BOUNDZERO, (void *)&pgs)); + err = extent_free(seg->ext, (u_long) pgs, sizeof(*slab), EX_BOUNDZERO); + ATF_REQUIRE_EQ(0, err); + } +#endif + + /* Now setup post boot */ + uvm.page_init_done = true; + + uvm_physseg_seg_chomp_slab(PHYSSEG_NODE_TO_HANDLE(seg), slab, npages * 2); + + /* Try alloc/free after uvm_page.c:uvm_page_init() as well */ + for (i = 0; i < npages; i++) { + ATF_REQUIRE_EQ(0, extent_alloc(seg->ext, sizeof(*slab), 1, 0, EX_BOUNDZERO, (void *)&pgs)); + err = extent_free(seg->ext, (u_long) pgs, sizeof(*slab), EX_BOUNDZERO); + ATF_REQUIRE_EQ(0, err); + } + +} + +ATF_TC(uvm_physseg_alloc_from_slab); +ATF_TC_HEAD(uvm_physseg_alloc_from_slab, tc) +{ + atf_tc_set_md_var(tc, "descr", "The slab alloc code.()"); + +} +ATF_TC_BODY(uvm_physseg_alloc_from_slab, tc) +{ + struct uvm_physseg *seg; + struct vm_page *slab, *pgs; + const size_t npages = UVM_PHYSSEG_BOOT_UNPLUG_MAX; /* Number of pages */ + + setup(); + + /* This is boot time */ + slab = malloc(sizeof(struct vm_page) * npages * 2); + + seg = uvm_physseg_alloc(sizeof(struct uvm_physseg)); + + uvm_physseg_seg_chomp_slab(PHYSSEG_NODE_TO_HANDLE(seg), slab, npages * 2); + + pgs = uvm_physseg_seg_alloc_from_slab(PHYSSEG_NODE_TO_HANDLE(seg), npages); + + ATF_REQUIRE(pgs != NULL); + + /* Now setup post boot */ + uvm.page_init_done = true; + +#if VM_PHYSSEG_MAX > 1 + pgs = uvm_physseg_seg_alloc_from_slab(PHYSSEG_NODE_TO_HANDLE(seg), npages); + ATF_REQUIRE(pgs != NULL); +#endif + atf_tc_expect_fail("alloc beyond extent"); + + pgs = uvm_physseg_seg_alloc_from_slab(PHYSSEG_NODE_TO_HANDLE(seg), npages); + ATF_REQUIRE(pgs != NULL); +} + +ATF_TC(uvm_physseg_init_seg); +ATF_TC_HEAD(uvm_physseg_init_seg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if uvm_physseg_init_seg adds pages to" + "uvmexp.npages"); +} +ATF_TC_BODY(uvm_physseg_init_seg, tc) +{ + struct uvm_physseg *seg; + struct vm_page *slab, *pgs; + const size_t npages = UVM_PHYSSEG_BOOT_UNPLUG_MAX; /* Number of pages */ + + setup(); + + /* This is boot time */ + slab = malloc(sizeof(struct vm_page) * npages * 2); + + seg = uvm_physseg_alloc(sizeof(struct uvm_physseg)); + + uvm_physseg_seg_chomp_slab(PHYSSEG_NODE_TO_HANDLE(seg), slab, npages * 2); + + pgs = uvm_physseg_seg_alloc_from_slab(PHYSSEG_NODE_TO_HANDLE(seg), npages); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + seg->start = 0; + seg->end = npages; + + seg->avail_start = 0; + seg->avail_end = npages; + + uvm_physseg_init_seg(PHYSSEG_NODE_TO_HANDLE(seg), pgs); + + ATF_REQUIRE_EQ(npages, uvmexp.npages); +} + +#if 0 +ATF_TC(uvm_physseg_init_seg); +ATF_TC_HEAD(uvm_physseg_init_seg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physload() \ + call works without a panic() after Segment is inited."); +} +ATF_TC_BODY(uvm_physseg_init_seg, tc) +{ + uvm_physseg_t upm; + psize_t npages = (VALID_END_PFN_1 - VALID_START_PFN_1); + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(0, uvmexp.npages); + + /* + * Boot time physplug needs explicit external init, + * Duplicate what uvm_page.c:uvm_page_init() does. + * Note: not everything uvm_page_init() does gets done here. + * Read the source. + */ + /* suck in backing slab, initialise extent. */ + uvm_physseg_seg_chomp_slab(upm, pgs, npages); + + /* + * Actual pgs[] allocation, from extent. + */ + uvm_physseg_alloc_from_slab(upm, npages); + + /* Now we initialize the segment */ + uvm_physseg_init_seg(upm, pgs); + + /* Done with boot simulation */ + extent_init(); + uvm.page_init_done = true; + + /* We have total memory of 1MB */ + ATF_CHECK_EQ(PAGE_COUNT_1M, uvmexp.npages); + + upm =uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + /* We added another 1MB so PAGE_COUNT_1M + PAGE_COUNT_1M */ + ATF_CHECK_EQ(PAGE_COUNT_1M + PAGE_COUNT_1M, uvmexp.npages); + +} +#endif + +ATF_TC(uvm_physseg_get_start); +ATF_TC_HEAD(uvm_physseg_get_start, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the start PFN is returned \ + correctly from a segment created via uvm_page_physload()."); +} +ATF_TC_BODY(uvm_physseg_get_start, tc) +{ + uvm_physseg_t upm; + + /* Fake early boot */ + setup(); + + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_start_invalid); +ATF_TC_HEAD(uvm_physseg_get_start_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_start() is called with invalid \ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_start_invalid, tc) +{ + /* Check for pgs == NULL */ + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + /* Invalid uvm_physseg_t */ + ATF_CHECK_EQ((paddr_t) -1, + uvm_physseg_get_start(UVM_PHYSSEG_TYPE_INVALID)); +} + +ATF_TC(uvm_physseg_get_end); +ATF_TC_HEAD(uvm_physseg_get_end, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the end PFN is returned \ + correctly from a segment created via uvm_page_physload()."); +} +ATF_TC_BODY(uvm_physseg_get_end, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_end_invalid); +ATF_TC_HEAD(uvm_physseg_get_end_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_end() is called with invalid \ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_end_invalid, tc) +{ + /* Check for pgs == NULL */ + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + /* Invalid uvm_physseg_t */ + ATF_CHECK_EQ((paddr_t) -1, + uvm_physseg_get_end(UVM_PHYSSEG_TYPE_INVALID)); +} + +ATF_TC(uvm_physseg_get_avail_start); +ATF_TC_HEAD(uvm_physseg_get_avail_start, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the avail_start PFN is \ + returned correctly from a segment created via uvm_page_physload()."); +} +ATF_TC_BODY(uvm_physseg_get_avail_start, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_1, uvm_physseg_get_avail_start(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2, uvm_physseg_get_avail_start(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_avail_start_invalid); +ATF_TC_HEAD(uvm_physseg_get_avail_start_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_avail_start() is called with invalid\ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_avail_start_invalid, tc) +{ + /* Check for pgs == NULL */ + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + /* Invalid uvm_physseg_t */ + ATF_CHECK_EQ((paddr_t) -1, + uvm_physseg_get_avail_start(UVM_PHYSSEG_TYPE_INVALID)); +} + +ATF_TC(uvm_physseg_get_avail_end); +ATF_TC_HEAD(uvm_physseg_get_avail_end, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the avail_end PFN is \ + returned correctly from a segment created via uvm_page_physload()."); +} +ATF_TC_BODY(uvm_physseg_get_avail_end, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_1, uvm_physseg_get_avail_end(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_avail_end_invalid); +ATF_TC_HEAD(uvm_physseg_get_avail_end_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_avail_end() is called with invalid\ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_avail_end_invalid, tc) +{ + /* Check for pgs == NULL */ + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + /* Invalid uvm_physseg_t */ + ATF_CHECK_EQ((paddr_t) -1, + uvm_physseg_get_avail_end(UVM_PHYSSEG_TYPE_INVALID)); +} + +ATF_TC(uvm_physseg_get_next); +ATF_TC_HEAD(uvm_physseg_get_next, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer values for next \ + segment using the uvm_physseg_get_next() call."); +} +ATF_TC_BODY(uvm_physseg_get_next, tc) +{ + uvm_physseg_t upm; +#if VM_PHYSSEG_MAX > 1 + uvm_physseg_t upm_next; +#endif + + /* We insert the segments in ascending order */ + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID_OVERFLOW, + uvm_physseg_get_next(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm_next = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + upm = uvm_physseg_get_next(upm); /* Fetch Next */ + + ATF_CHECK_EQ(upm_next, upm); + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + upm_next = uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(3, uvm_physseg_get_entries()); + + upm = uvm_physseg_get_next(upm); /* Fetch Next */ + + ATF_CHECK_EQ(upm_next, upm); + ATF_CHECK_EQ(VALID_START_PFN_3, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_3, uvm_physseg_get_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_next_invalid); +ATF_TC_HEAD(uvm_physseg_get_next_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_next() is called with invalid \ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_next_invalid, tc) +{ + uvm_physseg_t upm = UVM_PHYSSEG_TYPE_INVALID; + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID, uvm_physseg_get_next(upm)); +} + +ATF_TC(uvm_physseg_get_prev); +ATF_TC_HEAD(uvm_physseg_get_prev, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer values for previous \ + segment using the uvm_physseg_get_prev() call."); +} +ATF_TC_BODY(uvm_physseg_get_prev, tc) +{ +#if VM_PHYSSEG_MAX > 1 + uvm_physseg_t upm; +#endif + uvm_physseg_t upm_prev; + + + setup(); + upm_prev = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID_EMPTY, + uvm_physseg_get_prev(upm_prev)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + /* Fetch Previous, we inserted a lower value */ + upm = uvm_physseg_get_prev(upm); + + ATF_CHECK_EQ(upm_prev, upm); + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(3, uvm_physseg_get_entries()); + + /* + * This will return a UVM_PHYSSEG_TYPE_INVALID_EMPTY we are at the + * lowest + */ + upm = uvm_physseg_get_prev(upm); + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID_EMPTY, upm); +#endif +} + +ATF_TC(uvm_physseg_get_prev_invalid); +ATF_TC_HEAD(uvm_physseg_get_prev_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the invalid / error conditions \ + correctly when uvm_physseg_get_prev() is called with invalid \ + parameter values."); +} +ATF_TC_BODY(uvm_physseg_get_prev_invalid, tc) +{ + uvm_physseg_t upm = UVM_PHYSSEG_TYPE_INVALID; + + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID, uvm_physseg_get_prev(upm)); +} + +ATF_TC(uvm_physseg_get_first); +ATF_TC_HEAD(uvm_physseg_get_first, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer values for first \ + segment (lowest node) using the uvm_physseg_get_first() call."); +} +ATF_TC_BODY(uvm_physseg_get_first, tc) +{ + uvm_physseg_t upm = UVM_PHYSSEG_TYPE_INVALID_EMPTY; + uvm_physseg_t upm_first; + + /* Fake early boot */ + setup(); + + /* No nodes exist */ + ATF_CHECK_EQ(upm, uvm_physseg_get_first()); + + upm_first = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Pointer to first should be the least valued node */ + upm = uvm_physseg_get_first(); + ATF_CHECK_EQ(upm_first, upm); + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + /* Insert a node of lesser value */ + upm_first = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_CHECK_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + /* Pointer to first should be the least valued node */ + upm = uvm_physseg_get_first(); + ATF_CHECK_EQ(upm_first, upm); + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_1, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_1, uvm_physseg_get_avail_end(upm)); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + /* Insert a node of higher value */ + upm_first =uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_DEFAULT); + + ATF_CHECK_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(3, uvm_physseg_get_entries()); + + /* Pointer to first should be the least valued node */ + upm = uvm_physseg_get_first(); + ATF_CHECK(upm_first != upm); + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_1, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_1, uvm_physseg_get_avail_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_last); +ATF_TC_HEAD(uvm_physseg_get_last, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer values for last \ + segment using the uvm_physseg_get_last() call."); +} +ATF_TC_BODY(uvm_physseg_get_last, tc) +{ + uvm_physseg_t upm = UVM_PHYSSEG_TYPE_INVALID_EMPTY; + uvm_physseg_t upm_last; + + setup(); + + /* No nodes exist */ + ATF_CHECK_EQ(upm, uvm_physseg_get_last()); + + upm_last = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Pointer to last should be the most valued node */ + upm = uvm_physseg_get_last(); + ATF_CHECK_EQ(upm_last, upm); + ATF_CHECK_EQ(VALID_START_PFN_1, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_1, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_1, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_1, uvm_physseg_get_avail_end(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + /* Insert node of greater value */ + upm_last = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + /* Pointer to last should be the most valued node */ + upm = uvm_physseg_get_last(); + ATF_CHECK_EQ(upm_last, upm); + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + /* Insert node of greater value */ + upm_last = uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(3, uvm_physseg_get_entries()); + + /* Pointer to last should be the most valued node */ + upm = uvm_physseg_get_last(); + ATF_CHECK_EQ(upm_last, upm); + ATF_CHECK_EQ(VALID_START_PFN_3, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_3, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_3, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_3, uvm_physseg_get_avail_end(upm)); +#endif +} + +ATF_TC(uvm_physseg_valid); +ATF_TC_HEAD(uvm_physseg_valid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer value for current \ + segment is valid using the uvm_physseg_valid_p() call."); +} +ATF_TC_BODY(uvm_physseg_valid, tc) +{ + psize_t npages = (VALID_END_PFN_1 - VALID_START_PFN_1); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + uvm_physseg_init_seg(upm, pgs); + + ATF_REQUIRE_EQ(PAGE_COUNT_1M, uvmexp.npages); + + ATF_CHECK_EQ(true, uvm_physseg_valid_p(upm)); +} + +ATF_TC(uvm_physseg_valid_invalid); +ATF_TC_HEAD(uvm_physseg_valid_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the pointer value for current \ + segment is invalid using the uvm_physseg_valid_p() call."); +} +ATF_TC_BODY(uvm_physseg_valid_invalid, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + /* Invalid uvm_physseg_t */ + ATF_CHECK_EQ(false, uvm_physseg_valid_p(UVM_PHYSSEG_TYPE_INVALID)); + + /* + * Without any pages initialized for segment, it is considered + * invalid + */ + ATF_CHECK_EQ(false, uvm_physseg_valid_p(upm)); +} + +ATF_TC(uvm_physseg_get_highest); +ATF_TC_HEAD(uvm_physseg_get_highest, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned PFN matches \ + the highest PFN in use by the system."); +} +ATF_TC_BODY(uvm_physseg_get_highest, tc) +{ + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Only one segment so highest is the current */ + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_1 - 1, uvm_physseg_get_highest_frame()); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_DEFAULT); + + /* PFN_3 > PFN_1 */ + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_3 - 1, uvm_physseg_get_highest_frame()); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + /* PFN_3 > PFN_2 */ + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_3 - 1, uvm_physseg_get_highest_frame()); +#endif +} + +ATF_TC(uvm_physseg_get_free_list); +ATF_TC_HEAD(uvm_physseg_get_free_list, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned Free List type \ + of a segment matches the one returned from \ + uvm_physseg_get_free_list() call."); +} +ATF_TC_BODY(uvm_physseg_get_free_list, tc) +{ + uvm_physseg_t upm; + + /* Fake early boot */ + setup(); + + /* Insertions are made in ascending order */ + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_CHECK_EQ(VM_FREELIST_DEFAULT, uvm_physseg_get_free_list(upm)); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_FIRST16); + + ATF_CHECK_EQ(VM_FREELIST_FIRST16, uvm_physseg_get_free_list(upm)); +#endif + + /* This test will be triggered only if there are 3 or more segments. */ +#if VM_PHYSSEG_MAX > 2 + upm = uvm_page_physload(VALID_START_PFN_3, VALID_END_PFN_3, + VALID_AVAIL_START_PFN_3, VALID_AVAIL_END_PFN_3, VM_FREELIST_FIRST1G); + + ATF_CHECK_EQ(VM_FREELIST_FIRST1G, uvm_physseg_get_free_list(upm)); +#endif +} + +ATF_TC(uvm_physseg_get_start_hint); +ATF_TC_HEAD(uvm_physseg_get_start_hint, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned start_hint value \ + of a segment matches the one returned from \ + uvm_physseg_get_start_hint() call."); +} +ATF_TC_BODY(uvm_physseg_get_start_hint, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Will be Zero since no specific value is set during init */ + ATF_CHECK_EQ(0, uvm_physseg_get_start_hint(upm)); +} + +ATF_TC(uvm_physseg_set_start_hint); +ATF_TC_HEAD(uvm_physseg_set_start_hint, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned start_hint value \ + of a segment matches the one set by the \ + uvm_physseg_set_start_hint() call."); +} +ATF_TC_BODY(uvm_physseg_set_start_hint, tc) +{ + psize_t npages = (VALID_END_PFN_1 - VALID_START_PFN_1); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + uvm_physseg_init_seg(upm, pgs); + + ATF_CHECK_EQ(true, uvm_physseg_set_start_hint(upm, atop(128))); + + /* Will be atop(128) since no specific value is set above */ + ATF_CHECK_EQ(atop(128), uvm_physseg_get_start_hint(upm)); +} + +ATF_TC(uvm_physseg_set_start_hint_invalid); +ATF_TC_HEAD(uvm_physseg_set_start_hint_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned value is false \ + when an invalid segment matches the one trying to set by the \ + uvm_physseg_set_start_hint() call."); +} +ATF_TC_BODY(uvm_physseg_set_start_hint_invalid, tc) +{ + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + /* Force other check conditions */ + uvm.page_init_done = true; + + ATF_REQUIRE_EQ(true, uvm.page_init_done); + + ATF_CHECK_EQ(false, uvm_physseg_set_start_hint(upm, atop(128))); + + /* + * Will be Zero since no specific value is set after the init + * due to failure + */ + atf_tc_expect_signal(SIGABRT, "invalid uvm_physseg_t handle"); + + ATF_CHECK_EQ(0, uvm_physseg_get_start_hint(upm)); +} + +ATF_TC(uvm_physseg_get_pg); +ATF_TC_HEAD(uvm_physseg_get_pg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned vm_page struct \ + is correct when fetched by uvm_physseg_get_pg() call."); +} +ATF_TC_BODY(uvm_physseg_get_pg, tc) +{ + psize_t npages = (VALID_END_PFN_1 - VALID_START_PFN_1); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + struct vm_page *extracted_pg = NULL; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* Now we initialize the segment */ + uvm_physseg_init_seg(upm, pgs); + + ATF_REQUIRE_EQ(PAGE_COUNT_1M, uvmexp.npages); + + ATF_REQUIRE_EQ(NULL, extracted_pg); + + /* Try fetching the 5th Page in the Segment */ + extracted_pg = uvm_physseg_get_pg(upm, 5); + + /* Values of phys_addr is n * PAGE_SIZE where n is the page number */ + ATF_CHECK_EQ(5 * PAGE_SIZE, extracted_pg->phys_addr); + + /* Try fetching the 113th Page in the Segment */ + extracted_pg = uvm_physseg_get_pg(upm, 113); + + ATF_CHECK_EQ(113 * PAGE_SIZE, extracted_pg->phys_addr); +} + +#ifdef __HAVE_PMAP_PHYSSEG +ATF_TC(uvm_physseg_get_pmseg); +ATF_TC_HEAD(uvm_physseg_get_pmseg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned pmap_physseg \ + struct is correct when fetched by uvm_physseg_get_pmseg() call."); +} +ATF_TC_BODY(uvm_physseg_get_pmseg, tc) +{ + psize_t npages = (VALID_END_PFN_1 - VALID_START_PFN_1); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + struct pmap_physseg pmseg = { true }; + + struct pmap_physseg *extracted_pmseg = NULL; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* Now we initialize the segment */ + uvm_physseg_init_seg(upm, pgs); + + ATF_REQUIRE_EQ(PAGE_COUNT_1M, uvmexp.npages); + + ATF_REQUIRE_EQ(NULL, extracted_pmseg); + + ATF_REQUIRE_EQ(true, pmseg.dummy_variable); + + /* Extract the current pmseg */ + extracted_pmseg = uvm_physseg_get_pmseg(upm); + + /* + * We can only check if it is not NULL + * We do not know the value it contains + */ + ATF_CHECK(NULL != extracted_pmseg); + + extracted_pmseg->dummy_variable = pmseg.dummy_variable; + + /* Invert value to ensure test integrity */ + pmseg.dummy_variable = false; + + ATF_REQUIRE_EQ(false, pmseg.dummy_variable); + + extracted_pmseg = uvm_physseg_get_pmseg(upm); + + ATF_CHECK(NULL != extracted_pmseg); + + ATF_CHECK_EQ(true, extracted_pmseg->dummy_variable); +} +#endif + +ATF_TC(vm_physseg_find); +ATF_TC_HEAD(vm_physseg_find, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned segment number \ + is correct when an PFN is passed into uvm_physseg_find() call. \ + In addition to this the offset of the PFN from the start of \ + segment is also set if the parameter is passed in as not NULL."); +} +ATF_TC_BODY(vm_physseg_find, tc) +{ + psize_t offset = (psize_t) -1; + + uvm_physseg_t upm_first, result; +#if VM_PHYSSEG_MAX > 1 + uvm_physseg_t upm_second; +#endif + + setup(); + + upm_first = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + upm_second = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); +#endif + + /* Under ONE_MEGABYTE is segment upm_first */ + result = uvm_physseg_find(atop(ONE_MEGABYTE - 1024), NULL); + ATF_CHECK_EQ(upm_first, result); + ATF_CHECK_EQ(uvm_physseg_get_start(upm_first), + uvm_physseg_get_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_end(upm_first), + uvm_physseg_get_end(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_start(upm_first), + uvm_physseg_get_avail_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_end(upm_first), + uvm_physseg_get_avail_end(result)); + + ATF_REQUIRE_EQ((psize_t) -1, offset); + + /* This test will be triggered only if there are 2 or more segments. */ +#if VM_PHYSSEG_MAX > 1 + /* Over ONE_MEGABYTE is segment upm_second */ + result = uvm_physseg_find(atop(ONE_MEGABYTE + 8192), &offset); + ATF_CHECK_EQ(upm_second, result); + ATF_CHECK_EQ(uvm_physseg_get_start(upm_second), + uvm_physseg_get_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_end(upm_second), + uvm_physseg_get_end(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_start(upm_second), + uvm_physseg_get_avail_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_end(upm_second), + uvm_physseg_get_avail_end(result)); + + /* Offset is calculated based on PAGE_SIZE */ + /* atop(ONE_MEGABYTE + (2 * PAGE_SIZE)) - VALID_START_PFN1 = 2 */ + ATF_CHECK_EQ(2, offset); +#else + /* Under ONE_MEGABYTE is segment upm_first */ + result = uvm_physseg_find(atop(ONE_MEGABYTE - 12288), &offset); + ATF_CHECK_EQ(upm_first, result); + ATF_CHECK_EQ(uvm_physseg_get_start(upm_first), + uvm_physseg_get_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_end(upm_first), + uvm_physseg_get_end(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_start(upm_first), + uvm_physseg_get_avail_start(result)); + ATF_CHECK_EQ(uvm_physseg_get_avail_end(upm_first), + uvm_physseg_get_avail_end(result)); + + /* Offset is calculated based on PAGE_SIZE */ + /* atop(ONE_MEGABYTE - (3 * PAGE_SIZE)) - VALID_START_PFN1 = 253 */ + ATF_CHECK_EQ(253, offset); +#endif +} + +ATF_TC(vm_physseg_find_invalid); +ATF_TC_HEAD(vm_physseg_find_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the returned segment number \ + is (paddr_t) -1 when a non existant PFN is passed into \ + uvm_physseg_find() call."); +} +ATF_TC_BODY(vm_physseg_find_invalid, tc) +{ + psize_t offset = (psize_t) -1; + + setup(); + uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* No segments over 3 MB exists at the moment */ + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID, + uvm_physseg_find(atop(ONE_MEGABYTE * 3), NULL)); + + ATF_REQUIRE_EQ((psize_t) -1, offset); + + /* No segments over 3 MB exists at the moment */ + ATF_CHECK_EQ(UVM_PHYSSEG_TYPE_INVALID, + uvm_physseg_find(atop(ONE_MEGABYTE * 3), &offset)); + + ATF_CHECK_EQ((psize_t) -1, offset); +} + +ATF_TC(uvm_page_physunload_start); +ATF_TC_HEAD(uvm_page_physunload_start, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physunload()\ + call works without a panic(). Unloads from Start of the segment."); +} +ATF_TC_BODY(uvm_page_physunload_start, tc) +{ + /* + * Would uvmexp.npages reduce everytime an uvm_page_physunload is called? + */ + psize_t npages = (VALID_END_PFN_2 - VALID_START_PFN_2); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + uvm_physseg_init_seg(upm, pgs); + + ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + /* + * When called for first time, uvm_page_physload() removes the first PFN + * + * New avail start will be VALID_AVAIL_START_PFN_2 + 1 + */ + ATF_CHECK_EQ(VALID_START_PFN_2, atop(p)); + + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2 + 1, + uvm_physseg_get_avail_start(upm)); + + ATF_CHECK_EQ(VALID_START_PFN_2 + 1, uvm_physseg_get_start(upm)); + + /* Rest of the stuff should remain the same */ + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); +} + +ATF_TC(uvm_page_physunload_end); +ATF_TC_HEAD(uvm_page_physunload_end, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physunload()\ + call works without a panic(). Unloads from End of the segment."); +} +ATF_TC_BODY(uvm_page_physunload_end, tc) +{ + /* + * Would uvmexp.npages reduce everytime an uvm_page_physunload is called? + */ + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + /* Note: start != avail_start to remove from end. */ + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2 + 1, VALID_AVAIL_END_PFN_2, + VM_FREELIST_DEFAULT); + + p = 0; + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE( + uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm)); + + ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + /* + * Remember if X is the upper limit the actual valid pointer is X - 1 + * + * For example if 256 is the upper limit for 1MB memory, last valid + * pointer is 256 - 1 = 255 + */ + + ATF_CHECK_EQ(VALID_END_PFN_2 - 1, atop(p)); + + /* + * When called for second time, uvm_page_physload() removes the last PFN + * + * New avail end will be VALID_AVAIL_END_PFN_2 - 1 + * New end will be VALID_AVAIL_PFN_2 - 1 + */ + + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2 - 1, uvm_physseg_get_avail_end(upm)); + + ATF_CHECK_EQ(VALID_END_PFN_2 - 1, uvm_physseg_get_end(upm)); + + /* Rest of the stuff should remain the same */ + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2 + 1, + uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); +} + +ATF_TC(uvm_page_physunload_none); +ATF_TC_HEAD(uvm_page_physunload_none, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic uvm_page_physunload()\ + call works without a panic(). Does not unload from start or end \ + because of non-aligned start / avail_start and end / avail_end \ + respectively."); +} +ATF_TC_BODY(uvm_page_physunload_none, tc) +{ + psize_t npages = (VALID_END_PFN_2 - VALID_START_PFN_2); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + /* + * Note: start != avail_start and end != avail_end. + * + * This prevents any unload from occuring. + */ + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2 + 1, VALID_AVAIL_END_PFN_2 - 1, + VM_FREELIST_DEFAULT); + + p = 0; + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_REQUIRE( + uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm)); + + uvm_physseg_init_seg(upm, pgs); + + ATF_CHECK_EQ(false, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + /* uvm_page_physload() will no longer unload memory */ + ATF_CHECK_EQ(0, p); + + /* Rest of the stuff should remain the same */ + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2 + 1, + uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2 - 1, + uvm_physseg_get_avail_end(upm)); + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); +} + +ATF_TC(uvm_page_physunload_delete_start); +ATF_TC_HEAD(uvm_page_physunload_delete_start, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the uvm_page_physunload() \ + works when the segment gets small enough to be deleted scenario. \ + NOTE: This one works deletes from start."); +} +ATF_TC_BODY(uvm_page_physunload_delete_start, tc) +{ + /* + * Would uvmexp.npages reduce everytime an uvm_page_physunload is called? + */ + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + + /* + * Setup the Nuke from Starting point + */ + + upm = uvm_page_physload(VALID_END_PFN_1 - 1, VALID_END_PFN_1, + VALID_AVAIL_END_PFN_1 - 1, VALID_AVAIL_END_PFN_1, + VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ +#if VM_PHYSSEG_MAX > 1 + uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); +#endif + +#if VM_PHYSSEG_MAX == 1 + atf_tc_expect_signal(SIGABRT, + "cannot uvm_page_physunload() the last segment"); +#endif + + ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + ATF_CHECK_EQ(VALID_END_PFN_1 - 1, atop(p)); + + ATF_CHECK_EQ(1, uvm_physseg_get_entries()); + + /* The only node now is the one we inserted second. */ + upm = uvm_physseg_get_first(); + + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); +} + +ATF_TC(uvm_page_physunload_delete_end); +ATF_TC_HEAD(uvm_page_physunload_delete_end, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the uvm_page_physunload() \ + works when the segment gets small enough to be deleted scenario. \ + NOTE: This one works deletes from end."); +} +ATF_TC_BODY(uvm_page_physunload_delete_end, tc) +{ + /* + * Would uvmexp.npages reduce everytime an uvm_page_physunload is called? + */ + + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + + /* + * Setup the Nuke from Ending point + */ + + upm = uvm_page_physload(VALID_START_PFN_1, VALID_START_PFN_1 + 2, + VALID_AVAIL_START_PFN_1 + 1, VALID_AVAIL_START_PFN_1 + 2, + VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ +#if VM_PHYSSEG_MAX > 1 + uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); +#endif + +#if VM_PHYSSEG_MAX == 1 + atf_tc_expect_signal(SIGABRT, + "cannot uvm_page_physunload() the last segment"); +#endif + + ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + p = 0; + + ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + + ATF_CHECK_EQ(VALID_START_PFN_1 + 2, atop(p)); + + ATF_CHECK_EQ(1, uvm_physseg_get_entries()); + + /* The only node now is the one we inserted second. */ + upm = uvm_physseg_get_first(); + + ATF_CHECK_EQ(VALID_START_PFN_2, uvm_physseg_get_start(upm)); + ATF_CHECK_EQ(VALID_END_PFN_2, uvm_physseg_get_end(upm)); + ATF_CHECK_EQ(VALID_AVAIL_START_PFN_2, uvm_physseg_get_avail_start(upm)); + ATF_CHECK_EQ(VALID_AVAIL_END_PFN_2, uvm_physseg_get_avail_end(upm)); +} + +ATF_TC(uvm_page_physunload_invalid); +ATF_TC_HEAD(uvm_page_physunload_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the uvm_page_physunload() \ + fails when then Free list does not match."); +} +ATF_TC_BODY(uvm_page_physunload_invalid, tc) +{ + psize_t npages = (VALID_END_PFN_2 - VALID_START_PFN_2); + + struct vm_page *pgs = malloc(sizeof(struct vm_page) * npages); + + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + uvm_physseg_init_seg(upm, pgs); + + ATF_CHECK_EQ(false, uvm_page_physunload(upm, VM_FREELIST_FIRST4G, &p)); +} + +ATF_TC(uvm_page_physunload_force); +ATF_TC_HEAD(uvm_page_physunload_force, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the basic \ + uvm_page_physunload_force() including delete works without."); +} +ATF_TC_BODY(uvm_page_physunload_force, tc) +{ + /* + * Would uvmexp.npages reduce everytime an uvm_page_physunload is called? + */ + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_1, VALID_END_PFN_1, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_END_PFN_1, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ +#if VM_PHYSSEG_MAX > 1 + /* + * We have couple of physloads done this is bacause of the fact that if + * we physunload all the PFs from a given range and we have only one + * segment in total a panic() is called + */ + uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, + VALID_AVAIL_START_PFN_2, VALID_AVAIL_END_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); +#endif + +#if VM_PHYSSEG_MAX == 1 + atf_tc_expect_signal(SIGABRT, + "cannot uvm_page_physunload() the last segment"); +#endif + + ATF_REQUIRE_EQ(VALID_AVAIL_START_PFN_1, + uvm_physseg_get_avail_start(upm)); + + for(paddr_t i = VALID_AVAIL_START_PFN_1; + i < VALID_AVAIL_END_PFN_1; i++) { + ATF_CHECK_EQ(true, + uvm_page_physunload_force(upm, VM_FREELIST_DEFAULT, &p)); + ATF_CHECK_EQ(i, atop(p)); + + if(i + 1 < VALID_AVAIL_END_PFN_1) + ATF_CHECK_EQ(i + 1, uvm_physseg_get_avail_start(upm)); + } + + /* + * Now we try to retrieve the segment, which has been removed + * from the system through force unloading all the pages inside it. + */ + upm = uvm_physseg_find(VALID_AVAIL_END_PFN_1 - 1, NULL); + + /* It should no longer exist */ + ATF_CHECK_EQ(NULL, upm); + + ATF_CHECK_EQ(1, uvm_physseg_get_entries()); +} + +ATF_TC(uvm_page_physunload_force_invalid); +ATF_TC_HEAD(uvm_page_physunload_force_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests if the invalid conditions for \ + uvm_page_physunload_force_invalid()."); +} +ATF_TC_BODY(uvm_page_physunload_force_invalid, tc) +{ + paddr_t p = 0; + + uvm_physseg_t upm; + + setup(); + upm = uvm_page_physload(VALID_START_PFN_2, VALID_START_PFN_2+ 1, + VALID_START_PFN_2, VALID_START_PFN_2, VM_FREELIST_DEFAULT); + + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + ATF_REQUIRE_EQ(0, uvmexp.npages); + + ATF_CHECK_EQ(false, + uvm_page_physunload_force(upm, VM_FREELIST_DEFAULT, &p)); + + ATF_CHECK_EQ(0, p); +} + +ATF_TP_ADD_TCS(tp) +{ +#if defined(UVM_HOTPLUG) + /* Internal */ + ATF_TP_ADD_TC(tp, uvm_physseg_alloc_atboot_mismatch); + ATF_TP_ADD_TC(tp, uvm_physseg_alloc_atboot_overrun); + ATF_TP_ADD_TC(tp, uvm_physseg_alloc_sanity); + ATF_TP_ADD_TC(tp, uvm_physseg_free_atboot_mismatch); + ATF_TP_ADD_TC(tp, uvm_physseg_free_sanity); +#if VM_PHYSSEG_MAX > 1 + ATF_TP_ADD_TC(tp, uvm_physseg_atboot_free_leak); +#endif +#endif /* UVM_HOTPLUG */ + + ATF_TP_ADD_TC(tp, uvm_physseg_plug); + ATF_TP_ADD_TC(tp, uvm_physseg_unplug); + + /* Exported */ + ATF_TP_ADD_TC(tp, uvm_physseg_init); + ATF_TP_ADD_TC(tp, uvm_page_physload_preload); + ATF_TP_ADD_TC(tp, uvm_page_physload_postboot); + ATF_TP_ADD_TC(tp, uvm_physseg_handle_immutable); + ATF_TP_ADD_TC(tp, uvm_physseg_seg_chomp_slab); + ATF_TP_ADD_TC(tp, uvm_physseg_alloc_from_slab); + ATF_TP_ADD_TC(tp, uvm_physseg_init_seg); + ATF_TP_ADD_TC(tp, uvm_physseg_get_start); + ATF_TP_ADD_TC(tp, uvm_physseg_get_start_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_end); + ATF_TP_ADD_TC(tp, uvm_physseg_get_end_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_avail_start); + ATF_TP_ADD_TC(tp, uvm_physseg_get_avail_start_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_avail_end); + ATF_TP_ADD_TC(tp, uvm_physseg_get_avail_end_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_next); + ATF_TP_ADD_TC(tp, uvm_physseg_get_next_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_prev); + ATF_TP_ADD_TC(tp, uvm_physseg_get_prev_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_first); + ATF_TP_ADD_TC(tp, uvm_physseg_get_last); + ATF_TP_ADD_TC(tp, uvm_physseg_valid); + ATF_TP_ADD_TC(tp, uvm_physseg_valid_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_highest); + ATF_TP_ADD_TC(tp, uvm_physseg_get_free_list); + ATF_TP_ADD_TC(tp, uvm_physseg_get_start_hint); + ATF_TP_ADD_TC(tp, uvm_physseg_set_start_hint); + ATF_TP_ADD_TC(tp, uvm_physseg_set_start_hint_invalid); + ATF_TP_ADD_TC(tp, uvm_physseg_get_pg); + +#ifdef __HAVE_PMAP_PHYSSEG + ATF_TP_ADD_TC(tp, uvm_physseg_get_pmseg); +#endif + ATF_TP_ADD_TC(tp, vm_physseg_find); + ATF_TP_ADD_TC(tp, vm_physseg_find_invalid); + + ATF_TP_ADD_TC(tp, uvm_page_physunload_start); + ATF_TP_ADD_TC(tp, uvm_page_physunload_end); + ATF_TP_ADD_TC(tp, uvm_page_physunload_none); + ATF_TP_ADD_TC(tp, uvm_page_physunload_delete_start); + ATF_TP_ADD_TC(tp, uvm_page_physunload_delete_end); + ATF_TP_ADD_TC(tp, uvm_page_physunload_invalid); + ATF_TP_ADD_TC(tp, uvm_page_physunload_force); + ATF_TP_ADD_TC(tp, uvm_page_physunload_force_invalid); + + return atf_no_error(); +} diff --git a/sys/uvm/t_uvm_physseg_load.c b/sys/uvm/t_uvm_physseg_load.c new file mode 100644 index 000000000000..a150f6976c7b --- /dev/null +++ b/sys/uvm/t_uvm_physseg_load.c @@ -0,0 +1,740 @@ +/* $NetBSD: t_uvm_physseg_load.c,v 1.2 2016/12/22 08:15:20 cherry Exp $ */ + +/*- + * Copyright (c) 2015, 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Santhosh N. Raju and + * by Cherry G. Mathew + * + * 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. + */ + +#include +__RCSID("$NetBSD: t_uvm_physseg_load.c,v 1.2 2016/12/22 08:15:20 cherry Exp $"); + +/* + * If this line is commented out tests related touvm_physseg_get_pmseg() + * wont run. + * + * Have a look at machine/uvm_physseg.h for more details. + */ +#define __HAVE_PMAP_PHYSSEG + +/* + * This is a dummy struct used for testing purposes + * + * In reality this struct would exist in the MD part of the code residing in + * machines/vmparam.h + */ + +#ifdef __HAVE_PMAP_PHYSSEG +struct pmap_physseg { + int dummy_variable; /* Dummy variable use for testing */ +}; +#endif + +/* Testing API - assumes userland */ +/* Provide Kernel API equivalents */ +#include +#include +#include /* memset(3) et. al */ +#include /* printf(3) */ +#include /* malloc(3) */ +#include +#include +#include + +#define PRIxPADDR "lx" +#define PRIxPSIZE "lx" +#define PRIuPSIZE "lu" +#define PRIxVADDR "lx" +#define PRIxVSIZE "lx" +#define PRIuVSIZE "lu" + +#define UVM_HOTPLUG /* Enable hotplug with rbtree. */ +#define PMAP_STEAL_MEMORY +#define DEBUG /* Enable debug functionality. */ + +typedef unsigned long vaddr_t; +typedef unsigned long paddr_t; +typedef unsigned long psize_t; +typedef unsigned long vsize_t; + +#include +#include + +#ifndef DIAGNOSTIC +#define KASSERTMSG(e, msg, ...) /* NOTHING */ +#define KASSERT(e) /* NOTHING */ +#else +#define KASSERT(a) assert(a) +#define KASSERTMSG(exp, ...) printf(__VA_ARGS__); assert((exp)) +#endif + +#define VM_PHYSSEG_STRAT VM_PSTRAT_BSEARCH + +#define VM_NFREELIST 4 +#define VM_FREELIST_DEFAULT 0 +#define VM_FREELIST_FIRST16 3 +#define VM_FREELIST_FIRST1G 2 +#define VM_FREELIST_FIRST4G 1 + +/* + * Used in tests when Array implementation is tested + */ +#if !defined(VM_PHYSSEG_MAX) +#define VM_PHYSSEG_MAX 32 +#endif + +#define PAGE_SIZE 4096 +#define PAGE_SHIFT 12 +#define atop(x) (((paddr_t)(x)) >> PAGE_SHIFT) + +#define mutex_enter(l) +#define mutex_exit(l) + +#define _SYS_KMEM_H_ /* Disallow the real kmem API (see below) */ +/* free(p) XXX: pgs management need more thought */ +#define kmem_alloc(size, flags) malloc(size) +#define kmem_zalloc(size, flags) malloc(size) +#define kmem_free(p, size) free(p) + +psize_t physmem; + +struct uvmexp uvmexp; /* decl */ + +/* + * uvm structure borrowed from uvm.h + * + * Remember this is a dummy structure used within the ATF Tests and + * uses only necessary fields from the original uvm struct. + * See uvm/uvm.h for the full struct. + */ + +struct uvm { + /* vm_page related parameters */ + + bool page_init_done; /* TRUE if uvm_page_init() finished */ +} uvm; + +static void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + KASSERT(false); + + /*NOTREACHED*/ +} + +static void +uvm_pagefree(struct vm_page *pg) +{ + return; +} + +#if defined(UVM_HOTPLUG) +static void +uvmpdpol_reinit(void) +{ + return; +} +#endif /* UVM_HOTPLUG */ + +/* end - Provide Kernel API equivalents */ + +#include "uvm/uvm_physseg.c" + +#include + +#define ONE_MEGABYTE 1024 * 1024 + +/* Sample Page Frame Numbers */ +#define VALID_START_PFN_1 atop(0) +#define VALID_END_PFN_1 atop(ONE_MEGABYTE) +#define VALID_AVAIL_START_PFN_1 atop(0) +#define VALID_AVAIL_END_PFN_1 atop(ONE_MEGABYTE) + +#define VALID_START_PFN_2 atop(ONE_MEGABYTE + 1) +#define VALID_END_PFN_2 atop(ONE_MEGABYTE * 2) +#define VALID_AVAIL_START_PFN_2 atop(ONE_MEGABYTE + 1) +#define VALID_AVAIL_END_PFN_2 atop(ONE_MEGABYTE * 2) + +#define VALID_START_PFN_3 atop((ONE_MEGABYTE * 2) + 1) +#define VALID_END_PFN_3 atop(ONE_MEGABYTE * 3) +#define VALID_AVAIL_START_PFN_3 atop((ONE_MEGABYTE * 2) + 1) +#define VALID_AVAIL_END_PFN_3 atop(ONE_MEGABYTE * 3) + +#define VALID_START_PFN_4 atop(ONE_MEGABYTE + 1) +#define VALID_END_PFN_4 atop(ONE_MEGABYTE * 128) +#define VALID_AVAIL_START_PFN_4 atop(ONE_MEGABYTE + 1) +#define VALID_AVAIL_END_PFN_4 atop(ONE_MEGABYTE * 128) + +#define VALID_START_PFN_5 atop(ONE_MEGABYTE + 1) +#define VALID_END_PFN_5 atop(ONE_MEGABYTE * 256) +#define VALID_AVAIL_START_PFN_5 atop(ONE_MEGABYTE + 1) +#define VALID_AVAIL_END_PFN_5 atop(ONE_MEGABYTE * 256) + +/* + * Total number of pages (of 4K size each) should be 256 for 1MB of memory. + */ +#define PAGE_COUNT_1M 256 + +/* + * The number of Page Frames to allot per segment + */ +#define PF_STEP 8 + +/* + * A debug fucntion to print the content of upm. + */ + static inline void + uvm_physseg_dump_seg(uvm_physseg_t upm) + { +#if defined(DEBUG) + printf("%s: seg->start == %ld\n", __func__, + uvm_physseg_get_start(upm)); + printf("%s: seg->end == %ld\n", __func__, + uvm_physseg_get_end(upm)); + printf("%s: seg->avail_start == %ld\n", __func__, + uvm_physseg_get_avail_start(upm)); + printf("%s: seg->avail_end == %ld\n", __func__, + uvm_physseg_get_avail_end(upm)); + + printf("====\n\n"); +#else + return; +#endif /* DEBUG */ + } + +/* + * Private accessor that gets the value of vm_physmem.nentries + */ +static int +uvm_physseg_get_entries(void) +{ +#if defined(UVM_HOTPLUG) + return uvm_physseg_graph.nentries; +#else + return vm_nphysmem; +#endif /* UVM_HOTPLUG */ +} + +/* + * Note: This function replicates verbatim what happens in + * uvm_page.c:uvm_page_init(). + * + * Please track any changes that happen there. + */ +static void +uvm_page_init_fake(struct vm_page *pagearray, psize_t pagecount) +{ + uvm_physseg_t bank; + size_t n; + + for (bank = uvm_physseg_get_first(), + uvm_physseg_seg_chomp_slab(bank, pagearray, pagecount); + uvm_physseg_valid_p(bank); + bank = uvm_physseg_get_next(bank)) { + + n = uvm_physseg_get_end(bank) - uvm_physseg_get_start(bank); + uvm_physseg_seg_alloc_from_slab(bank, n); + uvm_physseg_init_seg(bank, pagearray); + + /* set up page array pointers */ + pagearray += n; + pagecount -= n; + } + + uvm.page_init_done = true; +} + +/* + * PHYS_TO_VM_PAGE: find vm_page for a PA. used by MI code to get vm_pages + * back from an I/O mapping (ugh!). used in some MD code as well. + */ +static struct vm_page * +uvm_phys_to_vm_page(paddr_t pa) +{ + paddr_t pf = atop(pa); + paddr_t off; + uvm_physseg_t psi; + + psi = uvm_physseg_find(pf, &off); + if (psi != UVM_PHYSSEG_TYPE_INVALID) + return uvm_physseg_get_pg(psi, off); + return(NULL); +} + +//static paddr_t +//uvm_vm_page_to_phys(const struct vm_page *pg) +//{ +// +// return pg->phys_addr; +//} + +/* + * XXX: To do, write control test cases for uvm_vm_page_to_phys(). + */ + +/* #define VM_PAGE_TO_PHYS(entry) uvm_vm_page_to_phys(entry) */ + +#define PHYS_TO_VM_PAGE(pa) uvm_phys_to_vm_page(pa) + +/* + * Test Fixture SetUp(). + */ +static void +setup(void) +{ + /* Prerequisites for running certain calls in uvm_physseg */ + uvmexp.pagesize = PAGE_SIZE; + uvmexp.npages = 0; + uvm.page_init_done = false; + uvm_physseg_init(); +} + +ATF_TC(uvm_physseg_100); +ATF_TC_HEAD(uvm_physseg_100, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 100 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_100, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 100; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_1K); +ATF_TC_HEAD(uvm_physseg_1K, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 1000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_1K, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 1000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_10K); +ATF_TC_HEAD(uvm_physseg_10K, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_10K, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 10000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_100K); +ATF_TC_HEAD(uvm_physseg_100K, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 100,000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_100K, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 100000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_1M); +ATF_TC_HEAD(uvm_physseg_1M, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 1,000,000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_1M, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 1000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_10M); +ATF_TC_HEAD(uvm_physseg_10M, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000,000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_10M, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 10000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_100M); +ATF_TC_HEAD(uvm_physseg_100M, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 100,000,000 calls, VM_PHYSSEG_MAX is 32."); +} +ATF_TC_BODY(uvm_physseg_100M, tc) +{ + paddr_t pa; + + setup(); + + for(paddr_t i = VALID_START_PFN_1; + i < VALID_END_PFN_1; i += PF_STEP) { + uvm_page_physload(i, i + PF_STEP, i, i + PF_STEP, + VM_FREELIST_DEFAULT); + } + + ATF_REQUIRE_EQ(VM_PHYSSEG_MAX, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(int i = 0; i < 100000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_1); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_1MB); +ATF_TC_HEAD(uvm_physseg_1MB, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000,000 calls, VM_PHYSSEG_MAX is 32 on 1 MB Segment."); +} +ATF_TC_BODY(uvm_physseg_1MB, t) +{ + paddr_t pa = 0; + + paddr_t pf = 0; + + psize_t pf_chunk_size = 0; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + + psize_t npages2 = (VALID_END_PFN_2 - VALID_START_PFN_2); + + struct vm_page *slab = malloc(sizeof(struct vm_page) * + (npages1 + npages2)); + + setup(); + + /* We start with zero segments */ + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_1, npages1, NULL)); + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2); + + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_2, npages2, NULL)); + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(pf = VALID_START_PFN_2; pf < VALID_END_PFN_2; pf += PF_STEP) { + pf_chunk_size = (psize_t) random() % (psize_t) (PF_STEP - 1) + 1; + uvm_physseg_unplug(pf, pf_chunk_size); + } + + for(int i = 0; i < 10000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_2); + if(pa < ctob(VALID_START_PFN_2)) + pa += ctob(VALID_START_PFN_2); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_64MB); +ATF_TC_HEAD(uvm_physseg_64MB, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000,000 calls, VM_PHYSSEG_MAX is 32 on 64 MB Segment."); +} +ATF_TC_BODY(uvm_physseg_64MB, t) +{ + paddr_t pa = 0; + + paddr_t pf = 0; + + psize_t pf_chunk_size = 0; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + + psize_t npages2 = (VALID_END_PFN_3 - VALID_START_PFN_3); + + struct vm_page *slab = malloc(sizeof(struct vm_page) * + (npages1 + npages2)); + + setup(); + + /* We start with zero segments */ + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_1, npages1, NULL)); + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2); + + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_3, npages2, NULL)); + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(pf = VALID_START_PFN_3; pf < VALID_END_PFN_3; pf += PF_STEP) { + pf_chunk_size = (psize_t) random() % (psize_t) (PF_STEP - 1) + 1; + uvm_physseg_unplug(pf, pf_chunk_size); + } + + for(int i = 0; i < 10000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_3); + if(pa < ctob(VALID_START_PFN_3)) + pa += ctob(VALID_START_PFN_3); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_128MB); +ATF_TC_HEAD(uvm_physseg_128MB, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000,000 calls, VM_PHYSSEG_MAX is 32 on 128 MB Segment."); +} +ATF_TC_BODY(uvm_physseg_128MB, t) +{ + paddr_t pa = 0; + + paddr_t pf = 0; + + psize_t pf_chunk_size = 0; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + + psize_t npages2 = (VALID_END_PFN_4 - VALID_START_PFN_4); + + struct vm_page *slab = malloc(sizeof(struct vm_page) + * (npages1 + npages2)); + + setup(); + + /* We start with zero segments */ + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_1, npages1, NULL)); + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2); + + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_2, npages2, NULL)); + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(pf = VALID_START_PFN_4; pf < VALID_END_PFN_4; pf += PF_STEP) { + pf_chunk_size = (psize_t) random() % (psize_t) (PF_STEP - 1) + 1; + uvm_physseg_unplug(pf, pf_chunk_size); + } + + for(int i = 0; i < 10000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_4); + if(pa < ctob(VALID_START_PFN_4)) + pa += ctob(VALID_START_PFN_4); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TC(uvm_physseg_256MB); +ATF_TC_HEAD(uvm_physseg_256MB, tc) +{ + atf_tc_set_md_var(tc, "descr", "Load test uvm_phys_to_vm_page() with \ + 10,000,000 calls, VM_PHYSSEG_MAX is 32 on 256 MB Segment."); +} +ATF_TC_BODY(uvm_physseg_256MB, t) +{ + paddr_t pa = 0; + + paddr_t pf = 0; + + psize_t pf_chunk_size = 0; + + psize_t npages1 = (VALID_END_PFN_1 - VALID_START_PFN_1); + + psize_t npages2 = (VALID_END_PFN_5 - VALID_START_PFN_5); + + struct vm_page *slab = malloc(sizeof(struct vm_page) * (npages1 + npages2)); + + setup(); + + /* We start with zero segments */ + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_1, npages1, NULL)); + ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); + + /* Post boot: Fake all segments and pages accounted for. */ + uvm_page_init_fake(slab, npages1 + npages2); + + ATF_REQUIRE_EQ(true, uvm_physseg_plug(VALID_START_PFN_2, npages2, NULL)); + ATF_REQUIRE_EQ(2, uvm_physseg_get_entries()); + + srandom((unsigned)time(NULL)); + for(pf = VALID_START_PFN_5; pf < VALID_END_PFN_5; pf += PF_STEP) { + pf_chunk_size = (psize_t) random() % (psize_t) (PF_STEP - 1) + 1; + uvm_physseg_unplug(pf, pf_chunk_size); + } + + for(int i = 0; i < 10000000; i++) { + pa = (paddr_t) random() % (paddr_t) ctob(VALID_END_PFN_5); + if(pa < ctob(VALID_END_PFN_5)) + pa += ctob(VALID_START_PFN_5); + PHYS_TO_VM_PAGE(pa); + } + + ATF_CHECK_EQ(true, true); +} + +ATF_TP_ADD_TCS(tp) +{ + /* Fixed memory size tests. */ + ATF_TP_ADD_TC(tp, uvm_physseg_100); + ATF_TP_ADD_TC(tp, uvm_physseg_1K); + ATF_TP_ADD_TC(tp, uvm_physseg_10K); + ATF_TP_ADD_TC(tp, uvm_physseg_100K); + ATF_TP_ADD_TC(tp, uvm_physseg_1M); + ATF_TP_ADD_TC(tp, uvm_physseg_10M); + ATF_TP_ADD_TC(tp, uvm_physseg_100M); + +#if defined(UVM_HOTPLUG) + /* Variable memory size tests. */ + ATF_TP_ADD_TC(tp, uvm_physseg_1MB); + ATF_TP_ADD_TC(tp, uvm_physseg_64MB); + ATF_TP_ADD_TC(tp, uvm_physseg_128MB); + ATF_TP_ADD_TC(tp, uvm_physseg_256MB); +#endif /* UVM_HOTPLUG */ + + return atf_no_error(); +} diff --git a/usr.bin/mixerctl/t_mixerctl.sh b/usr.bin/mixerctl/t_mixerctl.sh new file mode 100755 index 000000000000..94c0bdf8cdf4 --- /dev/null +++ b/usr.bin/mixerctl/t_mixerctl.sh @@ -0,0 +1,51 @@ +# $NetBSD: t_mixerctl.sh,v 1.1 2017/01/02 15:40:09 christos Exp $ + +atf_test_case noargs_usage +noargs_usage_head() { + atf_set "descr" "Ensure mixerctl(1) with no args prints a usage message" +} +noargs_usage_body() { + atf_check -s exit:0 -o not-empty -e ignore \ + mixerctl +} + +atf_test_case showvalue +showvalue_head() { + atf_set "descr" "Ensure mixerctl(1) can print the value for all variables" +} +showvalue_body() { + for var in $(mixerctl -a | awk -F= '{print $1}'); do + atf_check -s exit:0 -e ignore -o match:"^${var}=" \ + mixerctl ${var} + done +} + +atf_test_case nflag +nflag_head() { + atf_set "descr" "Ensure 'mixerctl -n' actually suppresses some output" +} +nflag_body() { + varname="$(mixerctl -a | head -1 | awk -F= '{print $1}')" + + atf_check -s exit:0 -o match:"${varname}" -e ignore \ + mixerctl ${varname} + + atf_check -s exit:0 -o not-match:"${varname}" -e ignore \ + mixerctl -n ${varname} +} + +atf_test_case nonexistant_device +nonexistant_device_head() { + atf_set "descr" "Ensure mixerctl(1) complains if provided a nonexistant mixer device" +} +nonexistant_device_body() { + atf_check -s not-exit:0 -o ignore -e match:"No such file" \ + mixerctl -d /a/b/c/d/e +} + +atf_init_test_cases() { + atf_add_test_case noargs_usage + atf_add_test_case showvalue + atf_add_test_case nflag + atf_add_test_case nonexistant_device +} diff --git a/usr.bin/uniq/d_basic.in b/usr.bin/uniq/d_basic.in new file mode 100644 index 000000000000..52a24bcfbb8c --- /dev/null +++ b/usr.bin/uniq/d_basic.in @@ -0,0 +1,4 @@ +1 +12 +1 +1 diff --git a/usr.bin/uniq/d_basic.out b/usr.bin/uniq/d_basic.out new file mode 100644 index 000000000000..d44fcba56f35 --- /dev/null +++ b/usr.bin/uniq/d_basic.out @@ -0,0 +1,3 @@ +1 +12 +1 diff --git a/usr.bin/uniq/d_counts.out b/usr.bin/uniq/d_counts.out new file mode 100644 index 000000000000..bb1c86ba3d6d --- /dev/null +++ b/usr.bin/uniq/d_counts.out @@ -0,0 +1,6 @@ + 1 #01 foo0 bar0 foo1 bar1 + 1 #02 bar0 foo1 bar1 foo1 + 1 #03 foo0 bar0 foo1 bar1 + 1 #04 + 2 #05 foo0 bar0 foo1 bar1 + 1 #07 bar0 foo1 bar1 foo0 diff --git a/usr.bin/uniq/d_input.in b/usr.bin/uniq/d_input.in new file mode 100644 index 000000000000..c32c44d44c0a --- /dev/null +++ b/usr.bin/uniq/d_input.in @@ -0,0 +1,7 @@ +#01 foo0 bar0 foo1 bar1 +#02 bar0 foo1 bar1 foo1 +#03 foo0 bar0 foo1 bar1 +#04 +#05 foo0 bar0 foo1 bar1 +#06 foo0 bar0 foo1 bar1 +#07 bar0 foo1 bar1 foo0 diff --git a/usr.bin/uniq/d_show_duplicates.out b/usr.bin/uniq/d_show_duplicates.out new file mode 100644 index 000000000000..45f4be7029a1 --- /dev/null +++ b/usr.bin/uniq/d_show_duplicates.out @@ -0,0 +1 @@ +#05 foo0 bar0 foo1 bar1 diff --git a/usr.bin/uniq/d_show_uniques.out b/usr.bin/uniq/d_show_uniques.out new file mode 100644 index 000000000000..1cc2ecbf3058 --- /dev/null +++ b/usr.bin/uniq/d_show_uniques.out @@ -0,0 +1,5 @@ +#01 foo0 bar0 foo1 bar1 +#02 bar0 foo1 bar1 foo1 +#03 foo0 bar0 foo1 bar1 +#04 +#07 bar0 foo1 bar1 foo0 diff --git a/usr.bin/uniq/t_uniq.sh b/usr.bin/uniq/t_uniq.sh new file mode 100755 index 000000000000..d90a4d595a86 --- /dev/null +++ b/usr.bin/uniq/t_uniq.sh @@ -0,0 +1,97 @@ +# $NetBSD: t_uniq.sh,v 1.1 2016/10/22 14:13:39 abhinav Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Abhinav Upadhyay +# +# 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. +# + +atf_test_case basic +basic_head() +{ + atf_set "descr" "Checks the basic functionality" +} +basic_body() +{ + atf_check -o file:$(atf_get_srcdir)/d_basic.out uniq \ + $(atf_get_srcdir)/d_basic.in +} + +atf_test_case test_counts +test_counts_head() +{ + atf_set "descr" "Tests the -c option, comparing each line of the input" \ + "file data starting from the second field" +} +test_counts_body() +{ + atf_check -o file:$(atf_get_srcdir)/d_counts.out uniq -c -f 1 \ + $(atf_get_srcdir)/d_input.in +} + +atf_test_case show_duplicates +show_duplicates_head() +{ + atf_set "descr" "Checks the -d option, comparing each line of the input" \ + "file data starting from the second field" +} +show_duplicates_body() +{ + atf_check -o file:$(atf_get_srcdir)/d_show_duplicates.out uniq -d -f 1 \ + $(atf_get_srcdir)/d_input.in +} + +atf_test_case show_uniques +show_uniques_head() +{ + atf_set "descr" "Checks the -u option, comparing each line of the input" \ + "file data starting from the second field" +} +show_uniques_body() +{ + atf_check -o file:$(atf_get_srcdir)/d_show_uniques.out uniq -u -f 1 \ + $(atf_get_srcdir)/d_input.in +} + +atf_test_case show_duplicates_from_third_character +show_duplicates_from_third_character_head() +{ + atf_set "descr" "Checks the -d option, comparing each line of the input" \ + "file data starting from the third character (-s option)" +} +show_duplicates_from_third_character_body() +{ + atf_check -o empty uniq -d -s 2 $(atf_get_srcdir)/d_input.in + +} + +atf_init_test_cases() +{ + atf_add_test_case basic + atf_add_test_case test_counts + atf_add_test_case show_duplicates + atf_add_test_case show_uniques + atf_add_test_case show_duplicates_from_third_character +}